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

# Errors

> Peel error types, what causes them, and how to recover.

Peel returns errors in two shapes:

* **HTTP errors** come from the Peel host itself, such as a wrong API key, a bad request shape, an API not found, or an endpoint that is not ready.
* **Runtime execution errors** come from a `:call` request where the compiled endpoint ran but failed to produce a successful result. These return the same normalized runtime envelope as successful calls. The outer HTTP status usually mirrors the envelope's `status_code`, so runtime errors can be `403`, `422`, `429`, `502`, `503`, `504`, or another target/upstream status rather than always `200`.
* **Build failures** appear on `GET /v1/apis/{api_id}` when an API has `status: "failed"` and on `GET /v1/apis/{api_id}/health` when health returns `status: "build_failed"`. These use `failure_reason_code` plus a structured `failure.artifact`, not the runtime response envelope.

For endpoint calls, check both layers: the HTTP status first, then `status`, `status_code`, and `error_code` if the response body is a runtime envelope. Host/setup errors may only include `error` or `code`; runtime errors include the normalized envelope. For failed builds, inspect `failure_reason_code` and `failure.artifact` on the API detail or failed-health response.

## HTTP errors

| Status                           | When it fires                                                                                                   | Fix                                                                                                                             |
| -------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `400`                            | Request body or path did not parse, such as bad JSON, an invalid `api_id`, or a missing required field.         | Validate against the endpoint's published schema in [Inspect the contract](/getting-started/quickstart#2-inspect-the-contract). |
| `401`                            | Missing or invalid `X-API-Key`.                                                                                 | Set the header on every request. See [Authentication](/getting-started/authentication).                                         |
| `403`                            | MCP OAuth scopes are insufficient for the tool you called.                                                      | Request the correct scope: `mcp:read`, `mcp:write`, or `mcp:tools`.                                                             |
| `404` on `GET /v1/apis/{api_id}` | The `api_id` does not exist for this account.                                                                   | Confirm the ID with `GET /v1/apis`.                                                                                             |
| `404` on `:call`                 | The `endpoint_name` is not published on this API. The response includes `available_endpoints`.                  | Copy a name from `available_endpoints` or re-inspect with `GET /v1/apis/{api_id}`.                                              |
| `409` on `:call`                 | The API is not `completed` yet, or a previous build or revise is still running. The response includes `status`. | Poll `GET /v1/apis/{api_id}` until `status` is `completed`.                                                                     |
| `409` on `/respond`              | The API is not currently in `needs_input`.                                                                      | Re-fetch the API. Only send `/respond` when `status` is `needs_input`.                                                          |
| `409` on exports                 | The API exists, but Peel has not published exportable endpoints yet. The response code is `API_NOT_EXPORTABLE`. | Inspect `GET /v1/apis/{api_id}` or `GET /v1/apis/{api_id}/health`, then wait, revise, or rebuild.                               |
| `429`                            | The build queue is saturated, or a per-actor rate limit was hit. The response includes a `Retry-After` header.  | Respect `Retry-After` and retry. See [Credits and limits](/reference/credits).                                                  |

## Runtime error codes

When `:call` returns a runtime envelope with `status: "error"` or `status: "timeout"`, the `error_code` field tells you what happened inside the compiled runtime. These are domain-specific errors: Peel reached the compiled endpoint execution path, but the compiled contract failed to produce data.

| `error_code`         | HTTP status     | Meaning                                                                                                                              | Fix                                                                                                                                                                                              |
| -------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `endpoint_not_found` | `404`           | The compiled endpoint refers to a stored step that no longer resolves.                                                               | Rebuild the API with `POST /v1/apis/{api_id}/rebuild`.                                                                                                                                           |
| `extraction_failed`  | `422`           | The transport succeeded but the parser could not extract a result from the response.                                                 | Check [API health](/features/api-updates#health-semantics). If degraded, rebuild.                                                                                                                |
| `timeout`            | `504`           | The target host took longer than the runtime budget.                                                                                 | Retry once. If it persists, rebuild. The target may have changed behavior.                                                                                                                       |
| `budget_exceeded`    | `402`           | The account is out of credits for this call.                                                                                         | Top up credits. See [Credits and limits](/reference/credits).                                                                                                                                    |
| `transport_error`    | `502`           | The underlying HTTP request to the target failed, such as connection reset, DNS, or TLS failure.                                     | Retry once. If it persists, the site may be down or blocking the compiled transport.                                                                                                             |
| `upstream_error`     | Upstream status | A deterministic replay endpoint reached the target API, but the target returned a non-success status such as `429`, `403`, or `5xx`. | Check `data.upstream_error_kind`. For `rate_limited`, wait or configure the target API key if the endpoint supports one. For `blocked`, revise the API to target a public deterministic surface. |
| `captcha_blocked`    | `403`           | The target responded with a CAPTCHA challenge, such as Cloudflare Turnstile, hCaptcha, reCAPTCHA, DataDome, or PerimeterX.           | Do not retry. Peel does not solve CAPTCHAs. Revise the API to target a different surface, such as the site's RSS or Atom feed, a public JSON endpoint, or a less-protected section.              |

Each runtime error also sets the outer HTTP status to the value in the table above, so you can branch on either layer.

## Build failures

Errors during build are visible in `GET /v1/apis/{api_id}` when `status` is `failed`. The same `failure` object is also present in `GET /v1/apis/{api_id}/health` when that route returns `status: "build_failed"`:

```json theme={null}
{
  "api": {
    "id": "4d0d8c18-3a06-4d53-9a57-2e5aa3f8c0cc",
    "status": "failed",
    "failure_reason_code": "compile_unpublishable_output",
    "failure": {
      "category": "compile_quality",
      "code": "compile_unpublishable_output",
      "message": "The compiler produced output that did not meet the publishable API quality bar.",
      "non_publishable_reason": "artifact_publishability_failed",
      "recommended_action": "revise_task",
      "retryable": false,
      "refund_reviewable": true,
      "artifact": {
        "attempted_strategies": ["custom_api_compiler"],
        "billing_disposition": "refund_review",
        "blocked_policy": null,
        "evidence_refs": ["evidence/rendered.html"],
        "failure_class": "runtime_verification_failed",
        "retryability": "not_retryable",
        "schema_version": 1,
        "stage": "agent_compile",
        "suggested_user_action": "revise_task",
        "support_debug_id": "api:4d0d8c18-3a06-4d53-9a57-2e5aa3f8c0cc:compile_unpublishable_output",
        "technical_reason": "Custom artifact publish gate failed: extract:quality_failed:singleton_endpoint_returned_collection",
        "user_facing_reason": "The compiled API did not pass runtime output verification."
      }
    }
  }
}
```

Use `failure_reason_code` for compact branching, `failure.non_publishable_reason` for the stable reason no API was published when one is available, and `failure.artifact` for support, billing, and retry decisions.

| `failure_reason_code`                 | Meaning                                                                                            | Fix                                                                                                                     |
| ------------------------------------- | -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `captcha_blocked` / `rate_limited`    | The target site blocked or throttled build-time access.                                            | Retry later for rate limits. For CAPTCHA or bot-wall blocks, revise toward a different public surface.                  |
| `empty_html` / `non_html_content`     | The URL did not provide usable page content for API generation.                                    | Try a different URL or revise toward a more specific public page.                                                       |
| `auth_surfaces_no_login`              | The requested data appears to require an authenticated session that this build could not compile.  | Revise toward public data, or use a supported session flow when available.                                              |
| `compile_unpublishable_output`        | Peel compiled a candidate artifact, but it did not pass the publishable API quality bar.           | Check `failure.non_publishable_reason` and `failure.artifact.technical_reason`, then revise with narrower instructions. |
| `action_flow_incomplete`              | Peel found an action-flow candidate, but deterministic action endpoint publication is still gated. | Revise toward a read-only public data task, or wait for action-flow support.                                            |
| `planner_no_plan` / `planner_blocked` | The build-time planner could not produce a deterministic endpoint plan.                            | Revise with clearer instructions or a more direct source URL.                                                           |
| `flow_definition_invalid`             | A generated deterministic flow was invalid before publication.                                     | Rebuild once. If it repeats, revise with a simpler task shape.                                                          |
| `extraction_proof_failed`             | The generated endpoint failed final extraction proofing before publication.                        | Revise with narrower fields or a more stable source page.                                                               |
| `build_timeout`                       | The build exceeded its internal time budget.                                                       | `rebuild`. If the target is slow to compile, raise `timeout_ms` on a fresh `POST /v1/apis` call, up to `120000`.        |
| `build_cost_budget_exceeded`          | The build exceeded the configured variable-cost budget before publication.                         | Revise toward a narrower task or contact support for operator review.                                                   |
| `unknown`                             | The build failed before Peel could classify it into a more specific code.                          | Contact support with the `api_id` and `failure.artifact.support_debug_id`.                                              |

`failure.artifact.failure_class` normalizes many lower-level codes into stable classes such as `captcha_or_botwall`, `auth_required`, `unsupported_action`, `no_matching_data`, `compile_policy_violation`, `runtime_verification_failed`, `build_timeout`, and `internal_error`. `failure.artifact.billing_disposition` tells you whether the build is not billable, needs refund review, or needs operator review.

Action-flow gates keep publication closed until Peel has compiled a deterministic, policy-checked request sequence. These failures use `failure.non_publishable_reason: "unsupported_action"` and `failure.artifact.failure_class: "unsupported_action"`.

When the build reaches the custom artifact publish gate, Peel has already compiled a deterministic runtime artifact and is deciding whether it is safe to publish. These failures keep `failure_reason_code: "compile_unpublishable_output"` and usually normalize to `failure.non_publishable_reason: "artifact_publishability_failed"`. Inspect `failure.artifact.technical_reason` and `failure.artifact.support_debug_id` for the precise operator diagnostic:

| Technical reason fragment                | Meaning                                                                                                                 | Fix                                                                                                                                    |
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `singleton_endpoint_returned_collection` | A detail-style endpoint returned a collection-shaped payload, so Peel refused to publish a misleading runtime contract. | Revise with collection-oriented instructions, or narrow the request to one entity/detail page.                                         |
| `endpoint_not_found`                     | The generated artifact did not contain the endpoint selected for publication.                                           | Rebuild. If it repeats, revise with a narrower endpoint name or task shape and include the `support_debug_id` when contacting support. |
| `missing_field:<field>`                  | The deterministic artifact ran, but the sample output did not include a required task field.                            | Revise with clearer field names or choose a URL where the requested field appears in the public evidence.                              |
| `too_few_items_for_collection:<count>`   | A collection endpoint produced fewer rows than the publish gate requires.                                               | Revise toward a broader listing page or rebuild if the source page changed.                                                            |

## Response envelope

All `:call` responses, successful or failed, share the same top-level envelope:

```json theme={null}
{
  "status": "success",
  "status_code": 200,
  "data": { },
  "error": null,
  "error_code": null,
  "execution_time": 842
}
```

On error:

```json theme={null}
{
  "status": "error",
  "status_code": 403,
  "data": null,
  "error": "Target responded with a CAPTCHA challenge",
  "error_code": "captcha_blocked",
  "execution_time": 1204
}
```

`status_code` inside the envelope mirrors the outer HTTP status so clients that only surface the body still have the code.

For deterministic replay endpoints, upstream HTTP failures keep their target status and include structured upstream details in `data` instead of projecting through the endpoint output schema:

```json theme={null}
{
  "status": "error",
  "status_code": 429,
  "data": {
    "error": "Too Many Requests",
    "upstream_error_code": "429",
    "upstream_error_kind": "rate_limited",
    "upstream_status_code": 429
  },
  "error": null,
  "error_code": "upstream_error",
  "execution_time": 391
}
```

On failures where the transport succeeded but the parser could not extract structured `data`, the envelope also includes a `raw_output` field: a truncated preview of the upstream response body. Use it to debug `extraction_failed` and `transport_error` outcomes:

```json theme={null}
{
  "status": "error",
  "status_code": 422,
  "data": null,
  "error": "Extractor produced no rows for endpoint 'get_top_stories'",
  "error_code": "extraction_failed",
  "execution_time": 1547,
  "raw_output": "<!DOCTYPE html>\n<html><head><title>Maintenance</title></head>\n<body><h1>We'll be right back</h1><p>Scheduled maintenance, please try again in a few minutes.</p></bo…"
}
```

`raw_output` is always clipped and is not a substitute for a proper re-parse. When it appears consistently on a previously healthy endpoint, rebuild the API.

## When an error means rebuild

Three signals usually point to rebuild as the right action:

1. `GET /v1/apis/{api_id}/health` returns `recommended_action: "rebuild"`.
2. `error_code` is `extraction_failed` or `endpoint_not_found` on a previously healthy endpoint.
3. Multiple endpoints on the same API start returning `transport_error` in a short window.

See [API updates](/features/api-updates) for the full decision tree.
