> ## Documentation Index
> Fetch the complete documentation index at: https://www.ayrshare.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Помилки GraphQL

> Як Ayrshare GraphQL API повідомляє про помилки — від кодів HTTP-статусів для некоректних запитів до масиву errors, часткового успіху, ідемпотентних повторних спроб і rate limits.

<h2 id="resolver-errors-arrive-with-http-200">
  Помилки резолверів надходять з HTTP 200
</h2>

Це найважливіше, що потрібно знати, перш ніж писати обробку помилок.

Відповідно до специфікації GraphQL over HTTP, запит, який дійшов до наших резолверів, повертає **HTTP 200, навіть якщо операція завершилася помилкою**. Про збій повідомляється в тілі відповіді, а не кодом статусу.

Тож така перевірка не працює:

```javascript theme={"system"}
// НЕПРАВИЛЬНО - невдалий запит буде сприйнято як успішний
if (response.ok) {
  return "everything worked";
}
```

Натомість перевіряйте тіло. Деякі транспортні помилки, як-от 415 для непідтримуваного `Content-Type`, мають порожнє тіло, тому перед парсингом перевірте, що відповідь є JSON:

```javascript theme={"system"}
if (!response.headers.get("content-type")?.includes("json")) {
  throw new Error(`GraphQL request failed with HTTP ${response.status}`);
}

const result = await response.json();

if (result.errors) {
  for (const error of result.errors) {
    // Помилки, перехоплені до виконання, не мають extensions, тому читайте їх безпечно.
    console.log(error.message, error.extensions?.status);
  }
}
```

Деякі збої перехоплюються ще до того, як щось виконується. Жоден із них не доходить до резолвера і не витрачає виклик API:

<ul class="custom-bullets">
  <li>Некоректний запит повертає статус помилки: <strong>HTTP 400</strong> для невалідного JSON або відсутнього <code>query</code>, <strong>413</strong> для тіла понад 64 KB, <strong>405</strong> для методу, відмінного від <code>POST</code>, і <strong>415</strong> для непідтримуваного <code>Content-Type</code>.</li>
  <li>Запит, невалідний щодо схеми, повертає <strong>HTTP 400</strong>, лише якщо ваш запит надсилає <code>Accept: application/graphql-response+json</code>. Це стосується синтаксичної помилки, невідомого поля, неправильного типу аргументу, некоректного значення enum і запиту, що перевищує <a href="/docs/apis/graphql/limits#query-limits">ліміти запитів</a>. Якщо ви надсилаєте <code>Accept: application/json</code> або не надсилаєте заголовок <code>Accept</code>, та сама помилка повертає <strong>HTTP 200</strong>. У будь-якому разі тіло містить масив <code>errors</code> і не містить <code>data</code>.</li>
</ul>

Щойно починається виконання, помилки валідації вхідних даних на рівні резолвера та збої API, зокрема автентифікації, авторизації, upstream rate limits та upstream-відповіді 5xx, повертають HTTP 200 з масивом `errors`.

<h2 id="the-error-shape">
  Структура помилки
</h2>

Кожна помилка резолвера містить свій HTTP-подібний статус API в `extensions`:

```json theme={"system"}
{
  "data": { "postHistory": null },
  "errors": [
    {
      "message": "Rate limit exceeded",
      "path": ["postHistory"],
      "extensions": {
        "status": 429,
        "retryAfter": 7
      }
    }
  ]
}
```

<ul class="custom-bullets">
  <li><code>message</code> — зрозумілий людині опис. Ми видаляємо API Key та інші облікові дані з повідомлень про помилки резолверів, але помилка щодо надісланого вами значення, наприклад змінної неправильного типу, може повторювати це значення. Перш ніж записувати <code>message</code> у логи, вважайте його потенційно чутливим.</li>
  <li><code>path</code> — яке поле у вашому запиті завершилося помилкою. Коли в одному запиті кілька полів, саме так ви дізнаєтеся, яке саме.</li>
  <li><code>extensions.status</code> — HTTP-статус, який повернув би відповідний REST-виклик. Орієнтуйтеся саме на нього.</li>
  <li><code>extensions.code</code> — числовий код помилки Ayrshare, якщо базовий ендпоінт його надає.</li>
  <li><code>extensions.retryAfter</code> — присутній в upstream-відповідях 429 щодо квоти або rate limit, коли доступна затримка перед повторною спробою. Він відсутній у відповіді 429 щодо бюджету викликів на запит.</li>
</ul>

Використовуйте `extensions.code` разом із [довідником кодів помилок](/docs/errors/overview) або передайте його в `explainError`.

<h2 id="partial-success">
  Частковий успіх
</h2>

Якщо запит запитує кілька речей і лише деякі з них успішні, ви отримуєте **і** `data`, **і** `errors`:

```graphql theme={"system"}
{
  history: postHistory(lastDays: 7)
  analytics: socialAnalytics(platforms: [INSTAGRAM])
}
```

```json theme={"system"}
{
  "data": {
    "history": { "history": [{ "id": "RkQ8uXg2jT7bNd1Wq0Ya", "status": "success", "post": "Hello world" }], "refId": "9d2a7c41f0b8e6d35a1c", "count": 1, "lastUpdated": "2026-09-24T12:00:00.000Z", "nextUpdate": "2026-09-24T12:00:00.000Z" },
    "analytics": null
  },
  "errors": [
    {
      "message": "Rate limit exceeded",
      "path": ["analytics"],
      "extensions": { "status": 429 }
    }
  ]
}
```

`history` виконано успішно, і його дані можна використовувати. Повторити потрібно лише `analytics`. Не відкидайте всю відповідь через наявність `errors`, бо так ви викинете дані, за які вже заплатили.

<h2 id="partial-success-on-multi-network-posts">
  Частковий успіх для дописів у кілька соцмереж
</h2>

Допис у кілька соцмереж може бути успішним в одних і завершитися помилкою в інших. Про це **не** повідомляється як про помилку GraphQL, тому що сама операція спрацювала: вона зробила саме те, що ви просили, і повідомляє, що сталося.

Результат розділяє три можливі наслідки:

```graphql theme={"system"}
mutation {
  createPost(input: {
    post: "Hi"
    platforms: [FACEBOOK, INSTAGRAM]
    idempotencyKey: "multi-network-hi-001"
  }) {
    status
    postIds { platform postUrl }
    errors { platform message code }
    blocked { platform message }
  }
}
```

<ul class="custom-bullets">
  <li><code>postIds</code> — соцмережі, у яких допис опубліковано, з URL дописів. Лише успішні.</li>
  <li><code>errors</code> — соцмережі, де сталася помилка, із причиною.</li>
  <li><code>blocked</code> — соцмережі, для яких публікацію зупинено до спроби, наприклад запобіжником (circuit breaker) для окремої соцмережі після повторюваних детермінованих збоїв. Вони відокремлені від спроб, у яких провайдер повернув помилки.</li>
</ul>

`status` має значення `"error"`, якщо **будь-яка** соцмережа завершилася помилкою, тому це не надійний сигнал того, що нічого не опубліковано. Щоб дізнатися, що саме опубліковано, читайте `postIds`.

<h2 id="retries">
  Повторні спроби
</h2>

**GraphQL API ніколи не повторює запити автоматично.** Якщо запит завершився помилкою, від вашого імені нічого не було непомітно спробувано ще раз.

Найбільше це важливо для mutations. `createPost` надає `idempotencyKey`: згенеруйте унікальний ключ перед першою спробою, а потім використовуйте той самий ключ і ідентичне корисне навантаження для кожної повторної спроби. Завершена повторна спроба відтворює початковий результат замість повторного запису. Не переходьте на новий ключ після неоднозначної відповіді.

`boostFacebookPost` і `instagramBoostPost` також приймають `idempotencyKey`, і він захищає реальні гроші. Повторна спроба з тим самим ключем і тими самими аргументами повертає початковий результат із позначкою `idempotentReplayed: true` замість того, щоб створити другу рекламу й заплатити за неї. Один ключ діє для всього вашого акаунта на обох платформах Meta, тому ніколи не використовуйте ключ просування у Facebook повторно для просування в Instagram.

Коли ключ відхилено, `extensions.status` повідомляє причину. `createPost` повертає 409, поки перша спроба ще обробляється, тож зачекайте й повторіть із тим самим ключем, і 400, коли корисне навантаження відрізняється від першої спроби. Mutations просування повертають 409 в обох випадках, а також коли ключ уже використовувався для просування на іншій платформі.

Інші операції запису, як-от `addComment`, не мають ключа ідемпотентності. Якщо їхню відповідь втрачено, перевірте стан у соцмережі або в API, перш ніж вирішувати, чи безпечна ще одна спроба.

Два різні ліміти повертають 429, і їх потрібно обробляти по-різному. Бюджет викликів відхиляє кореневі поля лише тоді, коли в тому самому запиті вже запущено п'ять викликів API, а його повідомлення починається з `Query exceeds the per-request dispatch budget`: розділіть ці поля на запити щонайбільше з п'ятьма полями, чекати не потрібно. Будь-яка інша відповідь 429 означає rate limit або квоту: зачекайте `extensions.retryAfter` секунд, якщо це поле присутнє, інакше збільшуйте затримку перед повторною спробою, і повторюйте лише поля, які завершилися помилкою. Для 5xx під час **читання** повторна спроба безпечна з одним винятком: `generatePost` — це query, але повторна спроба знову викликає AI-генератор, рахується як ще один виклик API і повертає інший текст. Для 5xx під час запису без ідемпотентності перед повторною спробою з'ясуйте, чи набула операція чинності.

<h2 id="common-statuses">
  Поширені статуси
</h2>

| `extensions.status` | Значення | Що робити |
| - | - | - |
| 400 | Некоректні вхідні дані, які відхилив наш API | Виправте запит; повторна спроба не допоможе |
| 401 | API Key відсутній або недійсний | Перевірте заголовок `Authorization` |
| 402 | План не включає цю функцію | Перевірте свій план |
| 403 | Ключ дійсний, але ця дія для нього не дозволена | Перевірте Profile Key і план |
| 404 | Запитаного об'єкта не існує | Перевірте id |
| 409 | Ключ ідемпотентності використовується: перший запит ще виконується, або ключ просування використано з іншими аргументами | Якщо перший запит ще виконується, зачекайте й повторіть без змін; інакше використайте новий ключ для нової операції |
| 429 | Rate limit, квота або бюджет викликів | Якщо повідомлення згадує бюджет викликів (dispatch budget), розділіть запит. Інакше зачекайте `retryAfter` або збільште затримку, якщо його немає |
| 500 | Щось пішло не так на нашому боці | Повторіть читання (але не `generatePost`, який тарифікується знову); використовуйте той самий ключ ідемпотентності, де це підтримується, інакше перевірте результат запису перед повторною спробою |

Помилки, які генерує сам GraphQL до виконання, як-от невідоме поле, неправильний тип аргументу або некоректне значення enum, не мають `extensions.status`. Вони повертають HTTP 400 або 200 залежно від вашого заголовка `Accept`, як описано вище. Вони означають, що неправильний сам запит, і жоден виклик API не було здійснено чи тарифіковано.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.