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

# Errores de GraphQL

> Cómo informa de los errores la API GraphQL de Ayrshare, desde los códigos de estado HTTP en solicitudes mal formadas hasta el array errors, el éxito parcial, los reintentos idempotentes y los rate limits.

<h2 id="resolver-errors-arrive-with-http-200">
  Los errores del resolver llegan con HTTP 200
</h2>

Esto es lo más importante que debes saber antes de escribir la gestión de errores.

Siguiendo la especificación GraphQL over HTTP, una solicitud que llega a nuestros resolvers devuelve **HTTP 200 incluso cuando una operación ha fallado**. El fallo se informa dentro del cuerpo de la respuesta, no mediante el código de estado.

Por lo tanto, esta comprobación no funciona:

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

Comprueba el cuerpo en su lugar. Algunos errores de transporte, como el 415 por un `Content-Type` no admitido, tienen el cuerpo vacío, así que comprueba que la respuesta sea JSON antes de analizarla:

```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);
  }
}
```

Algunos fallos se detectan antes de que se ejecute nada. Ninguno de ellos llega a un resolver ni consume una llamada a la API:

<ul class="custom-bullets">
  <li>Una solicitud mal formada devuelve un estado de error: <strong>HTTP 400</strong> por JSON no válido o una <code>query</code> ausente, <strong>413</strong> por un cuerpo de más de 64 KB, <strong>405</strong> por un método distinto de <code>POST</code> y <strong>415</strong> por un <code>Content-Type</code> no admitido.</li>
  <li>Una query que no es válida para el esquema devuelve <strong>HTTP 400</strong> solo si tu solicitud envía <code>Accept: application/graphql-response+json</code>. Esto incluye un error de sintaxis, un campo desconocido, un tipo de argumento incorrecto, un valor de enum no válido y una query que supera los <a href="/docs/apis/graphql/limits#query-limits">límites de consulta</a>. Si envías <code>Accept: application/json</code>, o ningún encabezado <code>Accept</code>, el mismo error devuelve <strong>HTTP 200</strong>. En ambos casos, el cuerpo tiene un array <code>errors</code> y ningún <code>data</code>.</li>
</ul>

Una vez que comienza la ejecución, la validación de entrada a nivel de resolver y los fallos de la API (incluidos autenticación, autorización, rate limits del servicio subyacente y respuestas 5xx del servicio subyacente) devuelven HTTP 200 con un array `errors`.

<h2 id="the-error-shape">
  La estructura del error
</h2>

Cada error del resolver incluye su estado de la API, similar a HTTP, en `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>: una descripción legible. Eliminamos las API Keys y otras credenciales de los mensajes de error del resolver, pero un error sobre un valor que enviaste, como una variable del tipo incorrecto, puede repetir ese valor. Trata <code>message</code> como posiblemente sensible antes de registrarlo.</li>
  <li><code>path</code>: qué campo de tu query falló. Con varios campos en una solicitud, así es como sabes cuál.</li>
  <li><code>extensions.status</code>: el estado HTTP que habría devuelto la llamada REST equivalente. Básate en este valor.</li>
  <li><code>extensions.code</code>: el código de error numérico de Ayrshare, cuando el endpoint subyacente proporciona uno.</li>
  <li><code>extensions.retryAfter</code>: presente en los 429 de cuota o rate limit del servicio subyacente cuando hay un tiempo de espera de reintento disponible. No está presente en el 429 del presupuesto de despacho por solicitud.</li>
</ul>

Usa `extensions.code` con la [referencia de códigos de error](/docs/errors/overview), o pásalo a `explainError`.

<h2 id="partial-success">
  Éxito parcial
</h2>

Si una solicitud pide varias cosas y solo algunas tienen éxito, obtienes **tanto** `data` como `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` tuvo éxito y sus datos se pueden usar. Solo hay que reintentar `analytics`. No descartes toda la respuesta porque haya `errors`: estarías tirando datos que ya se te han facturado.

<h2 id="partial-success-on-multi-network-posts">
  Éxito parcial en publicaciones en varias redes
</h2>

Una publicación en varias redes puede tener éxito en algunas y fallar en otras. Esto **no** se informa como un error de GraphQL, porque la operación en sí funcionó: hizo exactamente lo que pediste y te está indicando lo que ocurrió.

El resultado separa los tres desenlaces:

```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>: las redes en las que se publicó, con las URLs de sus publicaciones. Solo los éxitos.</li>
  <li><code>errors</code>: las redes que fallaron, con el motivo.</li>
  <li><code>blocked</code>: las redes que se detuvieron antes de intentarlo, por ejemplo por el circuit breaker por red tras fallos deterministas repetidos. Son distintas de los intentos ante el proveedor que devolvieron errores.</li>
</ul>

`status` es `"error"` si **cualquier** red falló, así que no es una señal fiable de que no se publicó nada. Lee `postIds` para saber qué se publicó.

<h2 id="retries">
  Reintentos
</h2>

**La API GraphQL nunca reintenta automáticamente.** Si una solicitud falla, no se ha vuelto a intentar nada en silencio en tu nombre.

Esto importa sobre todo en las mutations. `createPost` expone `idempotencyKey`: genera una clave única antes del primer intento y luego reutiliza la misma clave y una carga útil idéntica en cada reintento. Un reintento ya resuelto reproduce el resultado original en lugar de repetir la escritura. No cambies a una clave nueva tras una respuesta ambigua.

`boostFacebookPost` e `instagramBoostPost` también reciben un `idempotencyKey`, y este protege dinero real. Un reintento con la misma clave y los mismos argumentos devuelve el resultado original, marcado con `idempotentReplayed: true`, en lugar de crear y pagar un segundo anuncio. Una clave cubre toda tu cuenta en ambas plataformas de Meta, así que nunca reutilices la clave de una promoción de Facebook para una promoción de Instagram.

Cuando se rechaza una clave, `extensions.status` te indica el motivo. `createPost` devuelve 409 mientras el primer intento aún se está procesando, así que espera y reintenta con la misma clave, y 400 cuando la carga útil difiere de la del primer intento. Las mutations de promoción devuelven 409 en ambos casos, y también cuando la clave se usó para una promoción en la otra plataforma.

Otras escrituras, como `addComment`, no tienen clave de idempotencia. Si se pierde su respuesta, comprueba el estado de la red social o de la API antes de decidir si es seguro volver a intentarlo.

Dos límites distintos informan ambos de un 429, y requieren una gestión diferente. El presupuesto de despacho solo rechaza campos raíz una vez que ya se han iniciado cinco llamadas a la API en la misma solicitud, y su mensaje empieza por `Query exceeds the per-request dispatch budget`: divide esos campos en solicitudes de cinco como máximo, sin necesidad de esperar. Cualquier otro 429 es un rate limit o una cuota: espera `extensions.retryAfter` segundos cuando esté presente; de lo contrario, aplica un backoff antes de reintentar, y reintenta solo los campos que fallaron. Ante un 5xx en una **lectura**, reintentar es seguro, con una excepción: `generatePost` es una query, pero un reintento vuelve a llamar al generador de IA, cuenta como otra llamada a la API y devuelve un texto distinto. Ante un 5xx en una escritura sin idempotencia, verifica si surtió efecto antes de reintentar.

<h2 id="common-statuses">
  Estados habituales
</h2>

| `extensions.status` | Significado | Qué hacer |
| - | - | - |
| 400 | Entrada no válida que nuestra API rechazó | Corrige la solicitud; reintentar no servirá |
| 401 | API Key ausente o no válida | Revisa el encabezado `Authorization` |
| 402 | El plan no incluye esta funcionalidad | Revisa tu plan |
| 403 | Clave válida pero sin permiso para esta acción | Revisa las Profile Keys y el plan |
| 404 | Lo que pediste no existe | Verifica el id |
| 409 | La clave de idempotencia está en uso: la primera solicitud aún se está ejecutando, o se usó una clave de promoción con argumentos diferentes | Si la primera solicitud aún se está ejecutando, espera y reintenta sin cambios; de lo contrario, usa una clave nueva para la nueva operación |
| 429 | Rate limit, cuota o presupuesto de despacho | Si el mensaje menciona el presupuesto de despacho, divide la solicitud. De lo contrario, espera `retryAfter`, o aplica un backoff si no está presente |
| 500 | Algo salió mal en nuestro lado | Reintenta una lectura (no `generatePost`, que vuelve a facturar); reutiliza la misma clave de idempotencia cuando se admita; de lo contrario, verifica una escritura antes de reintentar |

Los errores que genera el propio GraphQL antes de la ejecución, como un campo desconocido, un tipo de argumento incorrecto o un valor de enum no válido, no tienen `extensions.status`. Devuelven HTTP 400 o 200 según tu encabezado `Accept`, como se describe arriba. Significan que la propia query es incorrecta, y no se realizó ni se facturó ninguna llamada a la API.


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