# Errors and status codes

Source: https://docs.qualflare.com/docs/api/errors · Last updated: 2026-11-03

Every error the Qualflare API returns: the JSON error body, each HTTP status and code with its message, the request_id for support, and when to retry.

When a Qualflare API request fails, the API answers with an HTTP status from 400 to 503 and a JSON body whose `code` field names the error, such as `auth.unauthorized` for a missing or expired token.

## Error body

The error body is a JSON object with a `code`, and a `message`, `request_id` and `fields` when they apply:

```json
{
  "code": "common.validation_failed",
  "message": "some fields are invalid",
  "request_id": "3f0c2a5e-8b1d-4c6e-9a7f-2d4b6e8c1a90",
  "fields": [{ "field": "name", "rule": "required", "message": "name is required" }]
}
```

| Field        | What it holds                                                          |
| ------------ | ---------------------------------------------------------------------- |
| `code`       | The error, always sent. Match on it in your code                       |
| `message`    | Text for people. Sent for `4xx` and plan errors, never for `5xx`       |
| `request_id` | The `X-Request-Id` of the response                                     |
| `fields`     | Validation errors only: a `field`, `rule` and `message` for each field |

The `request_id` is the value to quote to support.

## Status codes

| Status | Codes                                                                                                                           | Retry                     |
| ------ | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
| `400`  | `common.validation_failed`, `common.invalid_input`, `environment.not_found`, `milestone.not_found`, `common.resource_not_found` | No: fix the request       |
| `401`  | `auth.unauthorized`                                                                                                             | No: fix the token         |
| `402`  | `quota.exceeded`, `billing.subscription_inactive`, `billing.feature_unavailable`                                                | No                        |
| `403`  | `authz.project_access`, `authz.forbidden`                                                                                       | No                        |
| `404`  | `<resource>.not_found`, `common.resource_not_found`                                                                             | No                        |
| `405`  | `common.method_not_allowed`                                                                                                     | No                        |
| `409`  | `common.conflict`                                                                                                               | No                        |
| `413`  | `common.invalid_input`                                                                                                          | No: make the body smaller |
| `415`  | `common.invalid_input`                                                                                                          | No: send JSON             |
| `429`  | `common.rate_limited`                                                                                                           | Yes, after a pause        |
| `500`  | `common.unknown`, `common.service_error`                                                                                        | Yes                       |
| `503`  | `common.service_error`                                                                                                          | Yes                       |

### 400 Bad Request

* `common.validation_failed` with `some fields are invalid`, and one `fields` item per field, such as `name is required`, `name must be at most 255 characters` or `status must be one of: …`.
* `common.invalid_input` with `Content-Type must be application/json`, `Idempotency-Key must be at most 255 characters` or `invalid sort parameter`.
* On an upload: `environment.not_found` with `environment "<uid>" does not exist in this project`, `milestone.not_found` with `milestone #<n> does not exist in this project`, or `common.resource_not_found` with `language "<uid>" is not supported`. These are `400`, not `404`, so a wrong environment name never looks like a wrong URL.

### 401 Unauthorized

No token gives `access token is required`; a wrong, revoked or expired token gives `invalid or expired access token`. The response carries `WWW-Authenticate: Bearer realm="qualflare"`, with `error="invalid_token"` added when a token was sent. See [Why a token stops working](https://docs.qualflare.com/docs/api/access-tokens#why-a-token-stops-working).

### 402 Payment Required

A plan limit stopped the request: `quota.exceeded` with a message such as `Launches quota exceeded (<used>/<limit>)`, `billing.subscription_inactive` with `subscription is inactive or expired`, or `billing.feature_unavailable` with `feature '<key>' is not available on your plan`. See [What happens at a limit](https://docs.qualflare.com/docs/administration/plans#what-happens-at-a-limit).

### 403 Forbidden

* `authz.project_access` with `no project access`: the token's creator no longer has the role the request needs.
* `authz.forbidden` with `project is not accessible`: the token's project was deleted. A suspended project or workspace gives `authz.forbidden` with the reason.
* `authz.forbidden` with `this access token is read-only: it can read and upload test results, but changing cases, steps, defects or suites needs a token created with "Allow writes"`: a write other than an upload used a token created without **Allow writes**.

### 404 Not Found

A record that doesn't exist gives `<resource>.not_found`, such as `suite.not_found` with `suite not found` (also `case`, `launch`, `defect`, `test_plan` and `milestone`), or `common.resource_not_found` for a cluster or a step. A path the API doesn't have gives `common.resource_not_found` with `Not Found`.

### 405, 409, 413 and 415

* `405` with `common.method_not_allowed`: the path doesn't take that method.
* `409` with `common.conflict` and `resource already exists` (with the index name in parentheses when known) or `referenced resource constraint violated`: the record clashes with one that exists, or refers to one that doesn't.
* `413` with `common.invalid_input` and `Request Entity Too Large`: the body is over its limit ([Rate limits and request sizes](https://docs.qualflare.com/docs/api/limits#request-size)).
* `415` with `common.invalid_input` and `request body must be application/json`: the body is in another format.

### 429 Too Many Requests

Too many requests from one IP address get `{"code":"common.rate_limited"}`, with no `Retry-After` header. On `/mcp`, a token over its own limit gets the message `too many requests, please try again later` and `Retry-After: 1`. See [Limits](https://docs.qualflare.com/docs/api/limits#rate-limits).

### 500 and 503

Neither carries a `message`, only `code` and `request_id`. A `500` is `common.unknown`, or `common.service_error` for a database constraint violation. A `503` with `common.service_error` means the token check was busy, so retry. A database outage during the token check is a `500`, not a `401`, so a client retries instead of treating the token as revoked.

## Retry failed requests

* Retry `429`, `500`, `502`, `503` and `504` with a growing pause. `qf` does this itself, 3 times by default, with pauses from 1 to 30 seconds ([Retry & timeout](https://docs.qualflare.com/docs/cli/configuration#environment-variables)).
* Send an `Idempotency-Key` header on `POST /api/v1/collect` and `POST /api/v1/collect/ctrf` so a retried upload can't create a second launch. See [Retry safely with Idempotency-Key](https://docs.qualflare.com/docs/api/collect#idempotency-key). Other writes take no key: retrying a create that succeeded makes a second record.
* `qf` turns statuses into exit codes: see [Exit codes](https://docs.qualflare.com/docs/cli/troubleshooting#exit-codes).

## Related

* **Reference:** [Rate limits and request sizes](https://docs.qualflare.com/docs/api/limits) · [Access Tokens](https://docs.qualflare.com/docs/api/access-tokens) · [MCP server errors](https://docs.qualflare.com/docs/api/mcp-reference#errors)
* **CLI/API:** [Troubleshoot qf and CI uploads](https://docs.qualflare.com/docs/cli/troubleshooting) · [Qualflare API](https://docs.qualflare.com/docs/api)
