> ## 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.

# Lỗi GraphQL

> Cách Ayrshare GraphQL API báo cáo lỗi, từ mã trạng thái HTTP cho request sai định dạng đến mảng errors, thành công một phần, thử lại idempotent và giới hạn tốc độ.

<h2 id="resolver-errors-arrive-with-http-200">
  Lỗi resolver được trả về với HTTP 200
</h2>

Đây là điều quan trọng nhất cần biết trước khi bạn viết phần xử lý lỗi.

Theo đặc tả GraphQL over HTTP, một request đến được resolver của chúng tôi sẽ trả về **HTTP 200 ngay cả khi một thao tác thất bại**. Lỗi được báo cáo bên trong body phản hồi, không phải bằng mã trạng thái.

Vì vậy cách kiểm tra này không hoạt động:

```javascript theme={"system"}
// WRONG - will treat a failed request as a success
if (response.ok) {
  return "everything worked";
}
```

Thay vào đó hãy kiểm tra body. Một vài lỗi truyền tải, chẳng hạn 415 cho `Content-Type` không được hỗ trợ, có body rỗng, vì vậy hãy kiểm tra phản hồi có phải JSON không trước khi phân tích cú pháp:

```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) {
    // Errors caught before execution have no extensions, so read it safely.
    console.log(error.message, error.extensions?.status);
  }
}
```

Một số lỗi được phát hiện trước khi bất cứ thứ gì chạy. Không lỗi nào trong số đó đến được resolver hoặc tiêu tốn lệnh gọi API:

<ul class="custom-bullets">
  <li>Một request sai định dạng trả về mã trạng thái lỗi: <strong>HTTP 400</strong> cho JSON không hợp lệ hoặc thiếu <code>query</code>, <strong>413</strong> cho body lớn hơn 64 KB, <strong>405</strong> cho phương thức khác <code>POST</code>, và <strong>415</strong> cho <code>Content-Type</code> không được hỗ trợ.</li>
  <li>Một truy vấn không hợp lệ theo schema chỉ trả về <strong>HTTP 400</strong> nếu request của bạn gửi <code>Accept: application/graphql-response+json</code>. Điều này bao gồm lỗi cú pháp, trường không xác định, sai kiểu đối số, giá trị enum không hợp lệ, và truy vấn vượt quá <a href="/docs/apis/graphql/limits#query-limits">giới hạn truy vấn</a>. Nếu bạn gửi <code>Accept: application/json</code>, hoặc không có header <code>Accept</code>, cùng lỗi đó sẽ trả về <strong>HTTP 200</strong>. Dù theo cách nào, body cũng có một mảng <code>errors</code> và không có <code>data</code>.</li>
</ul>

Khi quá trình thực thi bắt đầu, các lỗi xác thực input ở cấp resolver và lỗi API — bao gồm xác thực, phân quyền, giới hạn tốc độ phía upstream và phản hồi 5xx phía upstream — đều trả về HTTP 200 kèm một mảng `errors`.

<h2 id="the-error-shape">
  Cấu trúc lỗi
</h2>

Mọi lỗi resolver đều mang trạng thái API tương tự HTTP trong `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> — mô tả dễ đọc cho con người. Chúng tôi loại bỏ API key và các thông tin xác thực khác khỏi thông báo lỗi resolver, nhưng một lỗi về giá trị bạn đã gửi, chẳng hạn một biến sai kiểu, có thể lặp lại giá trị đó. Hãy coi <code>message</code> là có thể nhạy cảm trước khi ghi log.</li>
  <li><code>path</code> — trường nào trong truy vấn của bạn bị lỗi. Khi có nhiều trường trong một request, đây là cách bạn biết trường nào.</li>
  <li><code>extensions.status</code> — mã trạng thái HTTP mà lệnh gọi REST tương đương sẽ trả về. Hãy rẽ nhánh xử lý dựa trên giá trị này.</li>
  <li><code>extensions.code</code> — mã lỗi Ayrshare dạng số, khi endpoint bên dưới cung cấp.</li>
  <li><code>extensions.retryAfter</code> — có mặt trong các lỗi 429 do hạn mức hoặc giới hạn tốc độ phía upstream khi có sẵn thời gian chờ thử lại. Nó không có mặt trong lỗi 429 do ngân sách dispatch theo từng request.</li>
</ul>

Hãy dùng `extensions.code` với [tài liệu tham khảo mã lỗi](/docs/errors/overview), hoặc truyền nó vào `explainError`.

<h2 id="partial-success">
  Thành công một phần
</h2>

Nếu một request yêu cầu nhiều thứ và chỉ một số thành công, bạn sẽ nhận được **cả** `data` và `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` đã thành công và dữ liệu của nó có thể sử dụng. Chỉ `analytics` cần thử lại. Đừng loại bỏ toàn bộ phản hồi chỉ vì có `errors` — làm vậy sẽ vứt bỏ dữ liệu mà bạn đã bị tính phí.

<h2 id="partial-success-on-multi-network-posts">
  Thành công một phần với bài đăng trên nhiều mạng xã hội
</h2>

Một bài đăng lên nhiều mạng xã hội có thể thành công trên một số mạng và thất bại trên những mạng khác. Điều này **không** được báo cáo là lỗi GraphQL, vì bản thân thao tác đã hoạt động — nó đã làm chính xác những gì bạn yêu cầu và đang cho bạn biết điều gì đã xảy ra.

Kết quả tách riêng ba kết cục:

```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> — các mạng xã hội đã xuất bản, cùng URL bài đăng. Chỉ bao gồm các trường hợp thành công.</li>
  <li><code>errors</code> — các mạng xã hội thất bại, kèm lý do.</li>
  <li><code>blocked</code> — các mạng xã hội bị dừng trước khi thử, ví dụ bởi circuit breaker theo từng mạng xã hội sau các lỗi xác định lặp lại. Những mục này tách biệt với các lần thử gửi đến nhà cung cấp đã trả về lỗi.</li>
</ul>

`status` là `"error"` nếu **bất kỳ** mạng xã hội nào thất bại, vì vậy nó không phải là tín hiệu đáng tin cậy cho thấy không có gì được xuất bản. Hãy đọc `postIds` để biết những gì đã được xuất bản.

<h2 id="retries">
  Thử lại
</h2>

**GraphQL API không bao giờ tự động thử lại.** Nếu một request thất bại, không có gì được âm thầm thử lại thay cho bạn.

Điều này quan trọng nhất đối với mutation. `createPost` cung cấp `idempotencyKey`: hãy tạo một key duy nhất trước lần thử đầu tiên, sau đó dùng lại cùng key đó và payload giống hệt cho mọi lần thử lại. Một lần thử lại đã hoàn tất sẽ phát lại kết quả ban đầu thay vì lặp lại thao tác ghi. Đừng chuyển sang key mới sau một phản hồi không rõ ràng.

`boostFacebookPost` và `instagramBoostPost` cũng nhận `idempotencyKey`, và nó bảo vệ tiền thật. Một lần thử lại với cùng key và cùng đối số sẽ trả về kết quả ban đầu, được đánh dấu `idempotentReplayed: true`, thay vì tạo và trả tiền cho một quảng cáo thứ hai. Một key áp dụng cho toàn bộ tài khoản của bạn trên cả hai nền tảng Meta, vì vậy đừng bao giờ dùng lại key của một lần boost Facebook cho một lần boost Instagram.

Khi một key bị từ chối, `extensions.status` cho bạn biết lý do. `createPost` trả về 409 khi lần thử đầu tiên vẫn đang được xử lý, vì vậy hãy chờ và thử lại với cùng key, và trả về 400 khi payload khác với lần thử đầu tiên. Các mutation boost trả về 409 trong cả hai trường hợp, và cả khi key đã được dùng cho một lần boost trên nền tảng còn lại.

Các thao tác ghi khác, chẳng hạn `addComment`, không có idempotency key. Nếu phản hồi của chúng bị mất, hãy kiểm tra mạng xã hội hoặc trạng thái API trước khi quyết định liệu thử lại có an toàn hay không.

Hai giới hạn khác nhau đều báo cáo 429, và chúng cần được xử lý khác nhau. Ngân sách dispatch chỉ từ chối các trường gốc khi đã có năm lệnh gọi API bắt đầu trong cùng một request, và thông báo của nó bắt đầu bằng `Query exceeds the per-request dispatch budget`: hãy chia các trường đó thành các request có tối đa năm trường, không cần chờ. Bất kỳ lỗi 429 nào khác là giới hạn tốc độ hoặc hạn mức: hãy chờ `extensions.retryAfter` giây khi có giá trị này, nếu không thì hãy lùi thời gian (back off) trước khi thử lại, và chỉ thử lại các trường đã thất bại. Với lỗi 5xx trên một thao tác **đọc**, việc thử lại là an toàn, với một ngoại lệ: `generatePost` là một query, nhưng một lần thử lại sẽ gọi lại trình tạo AI, được tính là một lệnh gọi API khác, và trả về văn bản khác. Với lỗi 5xx trên một thao tác ghi không có idempotency, hãy đối chiếu xem nó đã có hiệu lực chưa trước khi thử lại.

<h2 id="common-statuses">
  Các trạng thái phổ biến
</h2>

| `extensions.status` | Ý nghĩa | Cần làm gì |
| - | - | - |
| 400 | Input không hợp lệ bị API của chúng tôi từ chối | Sửa request; thử lại sẽ không giúp ích |
| 401 | Thiếu hoặc API key không hợp lệ | Kiểm tra header `Authorization` |
| 402 | Gói không bao gồm tính năng này | Kiểm tra gói của bạn |
| 403 | Key hợp lệ nhưng không được phép thực hiện hành động này | Kiểm tra profile key và gói |
| 404 | Thứ bạn yêu cầu không tồn tại | Xác minh id |
| 409 | Idempotency key đang được sử dụng: request đầu tiên vẫn đang chạy, hoặc một key boost đã được dùng với đối số khác | Nếu request đầu tiên vẫn đang chạy, hãy chờ và thử lại không thay đổi; nếu không, hãy dùng key mới cho thao tác mới |
| 429 | Giới hạn tốc độ, hạn mức, hoặc ngân sách dispatch | Nếu thông báo nhắc đến ngân sách dispatch, hãy chia nhỏ request. Nếu không, hãy chờ `retryAfter`, hoặc back off nếu không có giá trị này |
| 500 | Đã xảy ra sự cố ở phía chúng tôi | Thử lại thao tác đọc (không áp dụng cho `generatePost`, vì sẽ bị tính phí lần nữa); dùng lại cùng idempotency key khi được hỗ trợ, nếu không hãy xác minh thao tác ghi trước khi thử lại |

Các lỗi do chính GraphQL phát sinh trước khi thực thi, chẳng hạn trường không xác định, sai kiểu đối số, hoặc giá trị enum không hợp lệ, không có `extensions.status`. Chúng trả về HTTP 400 hoặc 200 tùy thuộc vào header `Accept` của bạn, như đã mô tả ở trên. Chúng có nghĩa là bản thân truy vấn sai, và không có lệnh gọi API nào được thực hiện hoặc tính phí.


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