Skip to content
The docs are now organized by product area. Old links still work. Report a broken link

Errors and status codes

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.

Last updated

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:

{
  "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" }]
}
FieldWhat it holds
codeThe error, always sent. Match on it in your code
messageText for people. Sent for 4xx and plan errors, never for 5xx
request_idThe X-Request-Id of the response
fieldsValidation errors only: a field, rule and message for each field

The request_id is the value to quote to support.

Status codes

StatusCodesRetry
400common.validation_failed, common.invalid_input, environment.not_found, milestone.not_found, common.resource_not_foundNo: fix the request
401auth.unauthorizedNo: fix the token
402quota.exceeded, billing.subscription_inactive, billing.feature_unavailableNo
403authz.project_access, authz.forbiddenNo
404<resource>.not_found, common.resource_not_foundNo
405common.method_not_allowedNo
409common.conflictNo
413common.invalid_inputNo: make the body smaller
415common.invalid_inputNo: send JSON
429common.rate_limitedYes, after a pause
500common.unknown, common.service_errorYes
503common.service_errorYes

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.

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.

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).
  • 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.

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).
  • 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. Other writes take no key: retrying a create that succeeded makes a second record.
  • qf turns statuses into exit codes: see Exit codes.