# Errors (https://docs-ecmrhub-v2.lucaspiera.com/docs/errors)

> Error format, status codes and the codes an agent should handle, with what to do for each.

Every error is JSON with a stable `code` and a human `message`:

```json
{ "code": "VERSION_CONFLICT", "message": "..." }
```

The CLI and the SDK wrap it as `{"error":{"status","code","message","retryable"}}`, and add
`approval` for `428`. REST endpoints may add `details` and `hint`.

- **Decide by `code`, not by `message`.** Messages are written for people and are often in Polish.
- **`retryable` is true for `429` and `5xx`.** Repeat the same request with the same ids and
  idempotency keys. Other errors need a change before trying again.
- **`404` does not reveal other companies' data.** An id from another company looks like an id
  that does not exist.

## By status [#by-status]

| Status | Meaning | What to do |
| --- | --- | --- |
| 400 | The request is malformed | Fix the input; compare with the schema |
| 401 | No valid sign-in | Sign in again or ask for a new key; do not loop |
| 403 | Signed in, but not allowed | Tell the user what is missing |
| 404 | Not found, or not yours | Check the id |
| 409 | Conflict with the current state | Read the current state, then retry |
| 410 | Gone: deleted, expired or revoked | Do not retry; start a new one if needed |
| 413 | Too large | Send less |
| 422 | Valid JSON, incomplete or wrong for this step | Fill the named fields, or do the missing step first |
| 428 | A person must approve | Show the approval link, wait, repeat with the approval id |
| 429 | Rate limit | Wait and retry |
| 5xx | Server or network problem | Retry with the same keys |

## Codes to handle [#codes-to-handle]

| Code | Status | Meaning and action |
| --- | --- | --- |
| `UNAUTHENTICATED` | 401 | The session or token expired or was signed out. Sign in again. |
| `API_KEY_INVALID` | 401 | The API key is wrong, revoked or expired. Ask the user for a new key. |
| `CONNECTION_REVOKED` | 401 | The company disconnected this OAuth connection. A person must sign in again. **In build.** |
| `FORBIDDEN` | 403 | Not allowed for this kind of caller (for example, a person-only operation). |
| `API_KEY_FORBIDDEN` | 403 | The API key lacks the permission named in the message. The owner can create a key with it. |
| `CONNECTION_FORBIDDEN` | 403 | The OAuth connection lacks the permission, or the operation is for a person only. **In build.** |
| `APPROVAL_REJECTED` | 403 | The person rejected the approval. Ask the user what to change. **In build.** |
| `NOT_FOUND` | 404 | No such id in this company. |
| `INVALID_REQUEST` | 400 | The request body or parameters are wrong. |
| `INVALID_OPERATION_INPUT` | 400 | The input does not match the operation's schema. The message names the field. |
| `VERSION_CONFLICT` | 409 | The draft changed since your revision. Read it again and retry with the new revision. |
| `LIBRARY_VERSION_CONFLICT` | 409 | The address book changed meanwhile. Read it again and retry. |
| `IDEMPOTENCY_CONFLICT` | 409 | The same idempotency key was used with a different payload. Use a new key for a new write. |
| `DRAFT_ISSUED` | 409 | An issued document cannot be deleted. |
| `LINK_REPLACED` | 409 | A newer signing link for this role exists; the old request does nothing. |
| `APPROVAL_MISMATCH` | 409 | The input differs from what the person approved. Repeat the approved input, or request a new approval. **In build.** |
| `APPROVAL_CONSUMED` | 409 | The approval was already used; the operation already ran. **In build.** |
| `APPROVAL_NOT_FOUND` | 404 | No such approval for this company. **In build.** |
| `APPROVAL_LIMIT` | 429 | Too many approvals are waiting for this connection; ask the person to decide them. **In build.** |
| `DRAFT_DELETED` | 410 | The draft was deleted. |
| `GRANT_EXPIRED` | 410 | The signing link expired or was revoked. Create a new one. |
| `APPROVAL_EXPIRED` | 410 | The approval is older than 24 hours. Call again without the id. **In build.** |
| `PAYLOAD_TOO_LARGE` | 413 | The request is too big. |
| `INCOMPLETE_DOCUMENT` | 422 | Required fields are missing. Run `ecmrhub drafts check` and fill them. |
| `NOT_ISSUED` | 422 | Issue the document before creating a signing link. |
| `APPROVAL_REQUIRED` | 428 | A person must approve this exact call. See [Human approvals](https://docs-ecmrhub-v2.lucaspiera.com/docs/approvals.md). **In build.** |
| `TRANSPORT_ERROR` | 503 | No answer from the server. The write may or may not have happened; repeat with the same keys. |

## CLI exit codes [#cli-exit-codes]

The CLI maps errors to exit codes: `1` error, `2` usage, `3` not signed in, `4` missing
permission, `5` waiting for approval. See [CLI reference](https://docs-ecmrhub-v2.lucaspiera.com/docs/cli.md#exit-codes).
