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

# Errors and recovery

> Distinguish API request errors, processing failures, and business rejection codes, then choose the correct recovery action.

An EigenPal API request and the run it describes have separate outcomes.
**HTTP status describes the API request. `execution.status` describes the run.**
A successful `GET /api/v1/runs/{id}` returns HTTP `200` even when that run failed.
A workflow rejection code such as `422` is in `error.statusCode`, not the HTTP response status.

## API request errors

A non-2xx response means the API request could not be completed. Its body uses
`ApiErrorEnvelope`, distinct from a run's `error` object:

```json theme={null}
{
  "issues": [
    {
      "field": "input.document",
      "message": "A document is required",
      "code": "invalid_value",
      "severity": "error"
    }
  ],
  "requestId": "req_example"
}
```

The example illustrates a validation error. Actual messages and fields depend
on the request. Use `issues[].code` for programmatic decisions and
`issues[].field` to locate invalid input. Messages are explanatory text, not
stable identifiers. Optional `hint` and `docsUrl` fields provide more help.
Keep `requestId` for support and log correlation.

| HTTP status | Common issue codes | Meaning and response |
| - | - | - |
| 400 | `invalid_value`, `invalid`, validation-specific codes | Invalid body, parameters, input, or configuration. Fix the indicated fields before retrying. |
| 401 | `unauthorized` | Missing, invalid, or revoked credentials. Fix authentication. |
| 402 | `insufficient_credits` | Organization credit allowance exhausted. A run is not started. |
| 403 | `forbidden`, `api_trigger_disabled`, `manual_trigger_disabled` | Insufficient scope or disabled trigger. Correct permissions or trigger settings. |
| 404 | `not_found` | Resource missing or inaccessible. Check the ID, organization, and access. Inaccessible resources intentionally look missing. |
| 409 | Endpoint-specific | Conflicting state. Read the issue and reconcile before retrying. |
| 413 | Endpoint-specific | Request or upload too large. Use the supported upload flow or reduce the payload. |
| 429 | `rate_limited` | Request rate exceeded. Respect `Retry-After`. |
| 500 | `internal_error` | Unexpected server failure. Keep the request ID for investigation. |
| 503 | Endpoint-specific | Temporary service unavailability. Retry with backoff where safe. |

This lists common API errors; each endpoint's reference documents its supported
responses. Business status codes configured in a workflow are a separate set.
A network error or lost response does not prove a run was never created.
If a run ID is known, inspect that run before submitting it again. Do not assume
run creation is idempotent or that repeated POST requests cannot duplicate work.

## Accepted runs and HTTP success responses

For `POST /api/v1/runs` and `POST /api/v1/runs/{id}/rerun`:

| HTTP status | Meaning | Client action |
| - | - | - |
| 201 | Accepted asynchronously; `finished` is false. | Save `id`, then poll or use webhooks. |
| 202 | The requested wait expired before a terminal outcome. | Continue tracking the same run ID. The run was not cancelled. |
| 200 | A terminal outcome was reached within the wait window. | Inspect `execution.status` and `error`. |

`GET /api/v1/runs/{id}` returns `200` when it successfully retrieves a visible run,
regardless of processing outcome. A `404` means the run is missing or inaccessible.

`finished: true` includes `completed`, `failed`, `cancelled`, and `rejected`.
Only `execution.status: "completed"` indicates successful execution. `waiting`
is non-terminal and can represent a pause for human review. A successful
execution does not itself prove the extracted business values are correct.
`execution.schemaValid` is a separate schema verdict; null means no verdict was recorded.

## Structured run errors

A failed run can be retrieved with HTTP `200` and this outcome (other run
fields omitted):

```json theme={null}
{
  "id": "exec_example",
  "type": "workflow",
  "finished": true,
  "execution": {
    "status": "failed",
    "schemaValid": null,
    "batchId": null,
    "retry": { "number": 0, "previousRunId": null, "nextRun": null }
  },
  "error": {
    "code": "workflow_rejected",
    "statusCode": 422,
    "message": "The submitted document is not an acquisition agreement.",
    "step": "assert_contract",
    "retryable": false
  }
}
```

* `code`: machine-readable processing failure identifier.
* `message`: human-readable explanation. Do not parse it to identify failure types.
* `statusCode`: optional numeric business code authored in `control.fail`.
  It is not the status of the HTTP request and is absent for untyped failures.
* `step`: optional name of the `control.fail` step that rejected the run.
* `retryable`: another attempt might succeed. This does not schedule a retry.

The same error shape is used in run lists, run detail, and synchronous terminal
start/rerun responses. No `expand` is needed to obtain it. `error` is absent
while the run is in flight and null on terminal runs with no error recorded.
For phase, category, and provider diagnostics, request `?expand=debug` and
inspect `debug.observability.failure`. Operator messages and raw diagnostics
are not part of the public error object. Failed runs in Studio show the
copyable error payload in the Output section; run exports preserve it too.

### Processing failure codes

| Code | Meaning | Typical recovery |
| - | - | - |
| `workflow_rejected` | A `control.fail` business rule stopped processing. | Inspect `statusCode`, `step`, and the document or rule. No automatic retry. |
| `input_validation_failed` | Worker input did not match the input schema. | Correct input or workflow schema. |
| `input_file_resolution_failed` | An external input file could not be retrieved. | Check the source, reference, credentials, and availability. Retryability depends on the cause. |
| `input_file_not_found` | A run-owned input file is missing. | Check the file handoff. |
| `input_file_reference_invalid`, `input_file_reference_mismatch` | Invalid or mismatched run-owned file descriptor. | Correct the descriptor or handoff. |
| `input_scoped_file_resolution_failed` | A scoped input file could not be resolved. | Check the parent-to-child file handoff. |
| `output_schema_validation_failed` | Output did not match the required schema. | Review output, schema, and extraction configuration. |
| `llm_model_mismatch` | The configured model belongs to a different provider. | Correct the step's provider/model configuration. |
| `llm_model_not_found` | The provider did not recognize the requested model. | Check model availability and configuration. |
| `cancelled_by_user` | The run was cancelled. | Rerun only if intended. |
| `agent_limit_reached` | An agent reached its execution limit. | Review the agent's limits and task. |
| `<phase>_timeout`, `<phase>_failed` | Timeout or failure in an execution phase, such as `running_timeout`. | Inspect diagnostics and retryability. |
| `unknown_error` | Historical or untyped failure without a classified code. | Read the message and investigate using the run ID. |

This is a reference of common run-level codes, not a closed enum. New and
specialized codes can be added. Handle unfamiliar codes with a generic failure
path and retain the run ID. Individual step attempts may have more specific
provider or HTTP failure codes; those are distinct from the run-level code.

## Workflow-specific outcome documentation

Select a workflow and version in **Developers > Connect** to see every statically
declared Fail outcome, including its business code, declaring step, branch
conditions, and message template. The response selector includes each rejection,
standard processing failures (including input, output, model, and phase failures),
and cancellation alongside successful responses. Each documented outcome has
a response example. HTTP request errors have separate status codes, schemas,
and examples. Messages and retryability in standard examples are illustrative;
inspect the actual response.

The scoped **OpenAPI** download and **Swagger** preview include the same outcomes
under HTTP 200, with named rejection schemas and selectable response examples.
The `x-eigenpal-workflow-failures` operation extension carries the inventory for
tools. Changing the selected workflow version updates its declared outcomes.

Resolvable inline and waited child-workflow rejections are included. Dynamic,
unavailable, recursive targets, or inventories exceeding documentation limits
are explicitly marked as unresolved. Detached child-run outcomes belong to the
child's own documentation. Processing codes remain open to input, provider,
runtime, output, and system failures; always implement a generic failure path.

## Business rejections with control.fail

Use [Fail](https://docs.eigenpal.com/steps/control/fail.md) to reject an otherwise processable document:

```yaml theme={null}
- name: assert_contract
  type: control.fail
  condition: "steps.classify_contract.output.label != 'contract'"
  statusCode: 422
  message: 'The submitted document is not an acquisition agreement.'
```

The condition is optional. True stops the workflow; false skips this step.
Without a condition it always stops when reached. Codes can be integers from
400 through 599 and default to 422. Their business meaning is defined by the
workflow author. Even a configured 500 is a deliberate workflow rejection,
not proof of an API server error and not a reason to replay it automatically.

A `control.fail` outcome has `execution.status: "failed"` and
`error.code: "workflow_rejected"`; it does not set the execution status to
`rejected`. The latter is used for runs refused before processing, for example
by an email policy.

## Retries and reruns

In Studio, click the workflow name to open **Workflow settings > Retries**.
This sets the default for eligible steps. A step's **Inherit workflow default**
option shows the effective default and provides a **Workflow retry settings**
button. Step overrides take precedence.

There are separate retry mechanisms:

* **SDK HTTP retries:** retry API transport failures under the SDK's policy.
  A returned run with `execution.status: "failed"` does not throw an HTTP error
  or trigger SDK request retries.
* **File retrieval:** temporary source failures get up to two additional fetch
  attempts. Permanent reference, access, and configuration errors do not.
  Exhaustion fails the run; `retryable: true` does not create another run.
* **Durable step retries:** opt in at workflow or step level. Current support
  covers Website Reader and HTTP GET/HEAD, with a three-total-attempt worker
  ceiling and a five-minute elapsed budget. Unsafe writes, email, file outputs,
  and workflow invocations are not automatically replayed this way. AI providers
  can have separate request retries. Concurrent parallel branches have additional
  replay restrictions.
* **Reruns:** create a new run using the source input. They use the latest
  automation version unless `version=original` is requested. Consider external
  side effects before replaying an entire workflow.
* **Webhook retries:** retry notification delivery independently of processing.
  A failed delivery does not make a successful run fail. See
  [webhook delivery and retries](https://docs.eigenpal.com/api-reference/webhooks.md#delivery-and-retries).

## Migration from string errors and legacy endpoints

The unified Runs API now returns `error` as an object instead of a string.
Use `error.message` for display, `error.code` for classification, and
`error.statusCode` for a workflow's numeric business rejection code. Regenerate
or update API clients for the new schema.

Historical failures recover the business code and step when the stored failure
record retains the typed envelope. A plain historical message alone cannot
reconstruct a lost code and is returned as `unknown_error` without `statusCode`.

The legacy `POST /api/v1/workflows/{id}/run` compatibility endpoint retains its
string `error` and `executionId` response shape and returns 201 for accepted
runs, including when waiting. Migrate to `POST /api/v1/runs` and
`GET /api/v1/runs/{id}` for the structured contract described above.


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