# Errors

Every error has the same shape, with a machine-readable code and a hint about what to do next.

Every error, from every operation, has the same shape:

```json
{
  "error": {
    "code": "invalid_request",
    "message": "query.places.0: Invalid input: expected number, received string",
    "hint": "Fix the fields in `issues`. Ids for `places`, `roles` and `companies` come from the lookup endpoints.",
    "issues": [
      { "path": "query.places.0", "message": "Invalid input: expected number, received string" }
    ]
  }
}
```

- `code` is stable and machine-readable. Branch on it, not on `message`.
- `message` is for people and logs.
- `hint`, when present, says what to do next, often which lookup to call first. Agents can act on it directly.
- `issues`, on validation errors, lists one problem per invalid field, with a dotted `path` into your request.

## Codes

| Code              | Status | Meaning                                                              |
| ----------------- | ------ | -------------------------------------------------------------------- |
| `invalid_request` | 400    | The request doesn't match the spec. See `issues`.                    |
| `unauthorized`    | 401    | The operation needs you to be signed in (account operations).        |
| `forbidden`       | 403    | The operation isn't public.                                          |
| `not_found`       | 404    | No job, company or agent with that id or slug, or no such operation. |
| `conflict`        | 409    | The change conflicts with what exists, such as too many agents.      |
| `rate_limited`    | 429    | Too many requests. Wait and retry. Rate limits arrive with API keys. |
| `internal`        | 500    | Something went wrong on our side. Retry later.                       |
| `unavailable`     | 503    | A dependency is briefly unavailable. Retry later.                    |

## Not found is not an error in search

A search with unknown ids or slugs is valid and matches nothing; it returns `200` with no jobs. Check the response's `labels`: values that were not recognized are missing from it.

A closed job is not an error either. `GET /v1/jobs/{id}` returns closed jobs too, with `closedAt` set.
