> For the complete documentation index, see [llms.txt](https://docs.roboflow.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.roboflow.com/developer/errors-and-status-codes.md).

# Errors and Status Codes

Roboflow's developer tools share a small set of error categories, surfaced differently in each tool. This page is the cross-cutting reference.

## CLI exit codes

The CLI uses four well-defined exit codes so scripts and AI agents can branch on outcome without parsing output:

| Exit code | Meaning                                                                                                      |
| --------- | ------------------------------------------------------------------------------------------------------------ |
| `0`       | Success                                                                                                      |
| `1`       | General error (bad input, network failure, unexpected server response)                                       |
| `2`       | Authentication failure (missing or invalid API key, no workspace selected)                                   |
| `3`       | Resource not found (project, version, workflow, deployment, etc. doesn't exist or isn't visible to your key) |

In `--json` mode the CLI writes structured output to stdout on success and a JSON error object to stderr on failure, leaving stdout empty so pipelines stay safe to parse:

```bash
roboflow --json project get nonexistent 2>error.json
echo $?       # 3
cat error.json
# {"error": {"message": "Project 'nonexistent' not found", "hint": "Run 'roboflow project list' to see your projects."}}
```

## SDK exceptions

The Python SDK raises Python exceptions on failure. The most common types you'll encounter:

| Exception                                            | When                                                                                                                                              |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `RuntimeError`                                       | Operation is logically invalid - e.g., calling `restore()` on a project that isn't in Trash, or training on a version that hasn't been generated. |
| `ValueError`                                         | A passed argument is malformed - e.g., an unrecognized `model_format` for `Version.download()`.                                                   |
| `roboflow.adapters.rfapi.RoboflowError`              | The REST API returned a non-2xx response. The exception's string contains the server's error body.                                                |
| `roboflow.adapters.deploymentapi.DeploymentApiError` | Equivalent to `RoboflowError` for the dedicated-deployments service.                                                                              |
| `requests.exceptions.HTTPError` / `ConnectionError`  | Network-level failures (DNS, TLS, timeout).                                                                                                       |

Rule of thumb: catch `RuntimeError` for logical issues, `RoboflowError` for server-side rejections, and let everything else bubble.

```python
from roboflow.adapters import rfapi

try:
    project.restore()
except RuntimeError as e:
    print(f"Can't restore: {e}")
except rfapi.RoboflowError as e:
    print(f"Server rejected the request: {e}")
```

See [Logging and Debugging](/developer/python-sdk/logging-and-debugging.md) for how to inspect the underlying HTTP request when an exception isn't enough.

## REST API status codes

The REST API uses standard HTTP status codes. Roboflow-specific behavior:

| Status | Meaning                                                                                                                                                                                                                                                                                                                                                         |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200`  | Success. Response body is JSON.                                                                                                                                                                                                                                                                                                                                 |
| `204`  | Success, no body (used for some `PATCH` / `DELETE` endpoints).                                                                                                                                                                                                                                                                                                  |
| `400`  | Malformed request - missing required field, bad shape, invalid value.                                                                                                                                                                                                                                                                                           |
| `401`  | Authentication failure. No `api_key` was supplied, or the credential is invalid/revoked. This is **only** about whether the caller is authenticated - not what they're allowed to do.                                                                                                                                                                           |
| `402`  | Payment required. The workspace's plan does not support the requested operation. For inference, this means the model or architecture is only available on a credit-based plan, or the workspace's monthly Hosted API inference quota has been reached. The response body includes an `AccessException` error type.                                              |
| `403`  | Forbidden. The credential authenticated but isn't permitted the operation - e.g. it doesn't hold the required scope, or it lacks access to the target workspace or resource.                                                                                                                                                                                    |
| `404`  | Not found. The workspace, project, version, workflow, or other resource doesn't exist - **or** the credential authenticated but can't see it (lacks the required scope, or targets a resource/workspace it can't access). Roboflow deliberately hides resource existence, so a permission failure on many endpoints is reported as a `404` rather than a `403`. |
| `409`  | Conflict. The resource exists in a state that prevents the requested operation (for example, restoring a version whose parent project is also in Trash).                                                                                                                                                                                                        |
| `423`  | Locked. Workspace billing is paused - see the response body for the reason.                                                                                                                                                                                                                                                                                     |
| `429`  | Rate limited, or a plan quota was reached. A retryable rate limit has no `reason` field, so slow down and retry with exponential backoff. A plan-limit rejection carries a `reason` (ex: `project_limit_reached`) plus `limit` and `current` in the body: retrying won't help, upgrade the plan or free up the resource instead.                                |
| `5xx`  | Server error. Safe to retry with backoff.                                                                                                                                                                                                                                                                                                                       |

### Standard error body

Errors return JSON with at least a top-level `error` field:

```json
{
  "error": "Project 'nonexistent' not found"
}
```

Some endpoints also include a `hint` or a structured error object - see the per-endpoint documentation under [REST API](/developer/rest-api/using-the-rest-api.md) for specifics.

### Required scopes

API keys carry per-resource scopes. A credential that authenticates fine can still be missing the scope an operation needs - for example a key that can read a resource but lacks the corresponding `*:update` or `*:write` scope for a write. Because Roboflow hides resource existence, that shows up as a `404` (or, on some endpoints, a `403`) - **not** a `401`, which is reserved for missing or invalid credentials. See [Scoped API Keys](/developer/authentication/scoped-api-keys.md) for the scope reference.

## Cross-tool error mapping

| Situation                                                                               | CLI exit | SDK                                               | REST                    |
| --------------------------------------------------------------------------------------- | -------- | ------------------------------------------------- | ----------------------- |
| Missing / invalid / revoked credential                                                  | `2`      | `RoboflowError` ("401")                           | `401`                   |
| Authenticated but not permitted (missing scope, or no access to the resource/workspace) | `3`      | `RoboflowError` ("404") / `RoboflowError` ("403") | `404` (sometimes `403`) |
| Resource not found                                                                      | `3`      | `RoboflowError` ("404") / `RuntimeError`          | `404`                   |
| Plan feature unavailable / inference quota exceeded                                     | `1`      | `RoboflowError` ("402")                           | `402`                   |
| Rate limited or plan resource limit reached (ex: project count)                         | `1`      | `RoboflowError` ("429")                           | `429` (with `reason`)   |
| Bad input / malformed request                                                           | `1`      | `ValueError` / `RoboflowError` ("400")            | `400`                   |
| Server error / transient                                                                | `1`      | `RoboflowError` ("5xx")                           | `5xx`                   |

Use this table when wiring retries: a `2` / 401 should never be retried automatically (the credential won't get more valid), a `3` / 404 / 403 should never be retried (more attempts won't grant the missing access), but a `1` from a 5xx response is a candidate for retry with backoff.
