*MCP › Guías*

# Errores

> Un error de herramienta es un resultado con `isError: true` y el problema en el primer bloque de texto. Ramifica por su `code`.

## La forma de un error

El servidor informa de una llamada fallida como un resultado, no como un error JSON-RPC. El resultado lleva `isError: true` y el problema RFC 9457 de la API REST como JSON en `content[0].text`. No lleva `structuredContent`: la especificación exige que `structuredContent` coincida con el `outputSchema` de la herramienta, y un error no lo cumple.

*Un resultado 429*

```json
{
  "isError": true,
  "content": [
    {
      "type": "text",
      "text": "{\"type\":\"https://docs.filmo.io/errors/rate_limited\",\"title\":\"Rate limit exceeded\",\"status\":429,\"code\":\"rate_limited\",\"detail\":\"Rate limit exceeded: retry in 17 seconds\",\"requestId\":\"req_…\",\"retryAfter\":17,\"meta\":{\"rateLimit\":{\"limit\":120,\"remaining\":0,\"reset\":17}}}"
    }
  ]
}
```

El problema incluye `type`, `title`, `status`, `code`, `detail`, `requestId` y, cuando corresponden, `errors[]`, `requiredScope`, `requiredAddon` y `retryAfter`. Una llamada con clave de idempotencia también devuelve `meta.idempotencyKey` dentro del problema. Cita `requestId` cuando pidas ayuda. Es el mismo documento de problema que devuelve la API REST: consulta [Errores](https://docs.filmo.io/es/rest-api/guides/errors).

## Errores de protocolo

Una petición mal formada, un método desconocido o una herramienta desconocida es un error JSON-RPC con un código como `-32600`, `-32601`, `-32602` o `-32700`, y su mensaje está en inglés. Una llamada sin credencial no es JSON-RPC: es un HTTP 401 con un cuerpo de problema. Consulta la [referencia del protocolo](https://docs.filmo.io/es/mcp/reference/protocol).

## Leer un error con los SDK oficiales

`Client.callTool` del SDK de TypeScript y `call_tool` del SDK de Python devuelven el resultado; ninguno lanza una excepción por un error de herramienta. Comprueba `isError` y analiza el primer bloque de texto. Solo los fallos de protocolo, como una herramienta desconocida o un timeout, lanzan excepción.

**TypeScript**

```typescript
const result = await client.callTool({ name, arguments: args });
if (result.isError) {
  const text = (result.content as { type: string; text?: string }[]).find((c) => c.type === "text")?.text ?? "{}";
  const problem = JSON.parse(text); // { code, status, detail, requestId, retryAfter?, requiredScope? }
  if (problem.code === "rate_limited") await sleep(problem.retryAfter * 1000);
}
```

**Python**

```python
import asyncio, json

async def call(session, name, args):
    result = await session.call_tool(name, arguments=args)
    if result.isError:
        problem = json.loads(result.content[0].text)  # code, status, detail, requestId, retryAfter?, requiredScope?
        if problem["code"] == "rate_limited":
            await asyncio.sleep(problem["retryAfter"])
    return result
```

En los paquetes 2.x el campo es `is_error` en Python y el transporte se construye de otra forma; el contrato del servidor es el mismo. Consulta [Construir tu propio cliente](https://docs.filmo.io/es/mcp/connect/build-your-own).

## Ramificar según el código

| Código | Estado | Qué hacer |
| --- | --- | --- |
| `unauthorized` | 401 | Envía una credencial. Los códigos `api_key_*` indican por qué una clave no es válida: revocada, caducada o su propietario ya no es administrador. |
| `insufficient_scope` | 403 | A la clave o a la conexión le falta `requiredScope`. Mira más abajo. |
| `validation_error` | 400 | Corrige los campos indicados en `errors[]` y vuelve a llamar. |
| `not_found` | 404 | El registro no existe en esta cuenta. |
| `conflict` | 409 | Lee `errors[0].code` para saber la causa; vuelve a leer el registro antes de reintentar. |
| `precondition_failed` | 412 | El registro cambió desde que lo leíste. Vuelve a leerlo. |
| `idempotency_key_reused` | 422 | Esa clave se usó con argumentos distintos. Usa una clave nueva. |
| `rate_limited` | 429 | Espera `retryAfter` segundos. |
| `addon_required` | 402 | La cuenta necesita el add-on indicado en `requiredAddon`. |
| `service_unavailable` | 503 | La función está desactivada para la cuenta; reintentar no sirve de nada. |

## Permiso insuficiente

Una herramienta que necesita un permiso (scope) que la conexión no tiene devuelve [`insufficient_scope`](https://docs.filmo.io/es/rest-api/errors/insufficient_scope) con `requiredScope`. El servidor no responde con un 403 que pida al cliente ampliar sus permisos, así que ningún cliente añade el permiso por sí solo. Para añadir uno, elimina el conector y vuelve a añadirlo, y marca el permiso en la pantalla de consentimiento. Con una clave, crea otra clave con el permiso.

## Reintentos

Se aplica la tabla de decisión de la API REST. Un resultado te indica que es una repetición con `meta.idempotentReplay: true`.

| Estado | Idempotency-Key | Idempotent-Replay: true | Qué hacer |
| --- | --- | --- | --- |
| 409 conflict · in_progress | Sí | No | `errors[0].code` es `in_progress`: otra petición con la misma `Idempotency-Key` sigue en curso. Espera y reintenta con la misma clave (espera exponencial, hasta 3 veces): recibirás su respuesta guardada cuando termine. |
| 409 conflict · lock_timeout | — | No | `errors[0].code` es `lock_timeout`: otra escritura condicional retuvo el recurso más de 5 s. Reintenta con la misma clave y espera exponencial, hasta 3 veces; si persiste, vuelve a leer el recurso. |
| 409 conflict · invalid_state | — | — | `errors[0].code` es `invalid_state`: el estado del recurso impide la operación (una violación de unicidad, una línea de un lote atómico). No reintentes: esperar no lo arregla nunca. Vuelve a leer el recurso y decide según su estado actual. |
| 409 contact_in_use · address_in_use · insufficient_stock | — | — | No reintentes. `errors[]` dice qué bloquea la operación: corrígelo primero. |
| 412 precondition_failed | — | — | Vuelve a leer el recurso, rehaz tu cambio sobre él y envía el `ETag` nuevo. |
| 422 idempotency_key_reused | Sí | — | Es un fallo del cliente: la clave se usó con otro cuerpo. Usa una clave nueva solo si de verdad es otra operación. |
| 429 rate_limited | — | — | Espera los segundos de `Retry-After` y reintenta con la misma clave. |
| 500 internal_error | Sí | Sí | Para. El fallo ocurrió después del commit y se guarda y se repite durante 24 h con esa clave: lee el recurso para ver dónde quedó. |
| 500 internal_error | Sí | No | Reintenta con espera exponencial y la misma clave. |
| 500 internal_error | No | — | Lee antes de reintentar una escritura: sin clave nada te protege de un duplicado. |
| 503 service_unavailable | — | — | No reintentes: la función está desactivada en la cuenta. |
| 207 (`returnOrderLineItems` con `atomic: false`) | Sí | — | Las líneas que fallaron no se devolvieron y el resto sí. Reenvía solo las fallidas con una clave nueva; repetir la clave devolvería el mismo 207. |
| 413 · 502 · 504 (no es `application/problem+json`) | Sí | — | Respuesta de la plataforma, no de la API: decide por el estado y reintenta con la misma clave. Un 413 no se arregla reintentando (el cuerpo es demasiado grande). |
| Cualquier 4xx de validación | — | — | Corrige la petición y reenvíala; nunca en bucle. |
| 401 · 402 · 403 (clave, propietario o plan) | — | — | No reintentes: reenviar la misma petición vuelve a fallar. Actúa sobre la clave, su propietario o el plan: usa una clave válida con el permiso adecuado, pide a un administrador el permiso que falta o contrata el add-on. |

Cada código de error tiene su propia página en la pestaña de la API REST, con cuándo ocurre y qué hacer.

---

URL canónica: https://docs.filmo.io/es/mcp/guides/errors  
Última actualización: 2026-10-25