Errors
Every 4xx and 5xx response is an RFC 9457 (opens in a new tab) problem
details document with Content-Type: application/problem+json:
{
"type": "https://platform.opis.health/docs/errors/validation_failed",
"title": "Validation failed",
"status": 422,
"detail": "The request body has 2 invalid fields.",
"code": "validation_failed",
"request_id": "req_01k68a9nytdxx0x5x0p47zgrm8",
"errors": [
{
"pointer": "/items/2/grams",
"code": "too_large",
"detail": "Must be at most 5000."
},
{
"parameter": "If-Match",
"code": "invalid_format",
"detail": "Must be a quoted version, e.g. \"3\"."
}
]
}| Field | Use it for |
|---|---|
code | Branching in code. A stable, machine-readable value from the table below. |
status | The HTTP status, repeated for convenience. |
title | A short summary, stable per code. |
detail | A human-readable explanation of this occurrence — for logs and developers, not end users. Never parse it. |
request_id | Quote it when you contact support. Also in the Request-Id header of every response. |
errors[] | Per-field failures: a JSON pointer into the body (/images/0/url) or a parameter name, plus a field code and detail. |
current_state | On invalid_state and precondition_failed: the resource's status, version, cancellation_reason and confirmation_method, so you can reconcile without another GET. |
type | A URL that documents the code — each one links to a page on this site. |
The code list is append-only within /v1. Codes are added but never renamed or removed. Treat an
unknown code as a generic error of its HTTP status class.
Field codes in errors[]
required, not_allowed, invalid_type, invalid_format, invalid_value, too_small,
too_large, too_short, too_long, duplicate (for example an item id twice in a confirmation),
unknown_reference (an itm_ id that is not in the identification). Business rules about one field
use the problem's own code instead, such as use_reject_instead.
HTTP error codes
| Code | Status | Meaning |
|---|---|---|
validation_failed | 422 | The request is well formed but a body field, query parameter or header failed validation. errors[] lists each failure with a JSON pointer or parameter name. |
invalid_request | 400 | The request cannot be parsed: malformed JSON or multipart body, an invalid pagination cursor, or an unparseable If-Match / If-None-Match header. |
unauthorized | 401 | The Authorization header is missing, or the API key is unknown, revoked or expired. |
forbidden | 403 | The key is valid but lacks the scope this operation needs, or the caller’s IP address is not on the key’s allowlist. |
not_found | 404 | The resource or route does not exist, belongs to another project or mode, or was deleted. |
conflict | 409 | The request conflicts with existing state not covered by a more specific code (for example the webhook-endpoint limit is reached). |
invalid_state | 409 | The operation is not allowed in the resource’s current status — for example confirming an analysis that is no longer requires_confirmation. The problem carries current_state. |
precondition_failed | 412 | If-Match does not match the resource’s current ETag: it changed since you read it. The problem carries current_state. |
precondition_required | 428 | The project requires If-Match on this operation and it was not sent. |
idempotency_key_reused | 422 | The Idempotency-Key was already used with a different request (method, path or body). |
idempotency_request_in_progress | 409 | A request with the same Idempotency-Key is still being processed. |
rate_limited | 429 | The key’s request-rate limit was exceeded. |
quota_exceeded | 429 | A daily or monthly quota for a meter (identification or nutrition) is used up. |
spend_limit_reached | 402 | The organization’s monthly spend cap would be exceeded. |
too_many_pending_confirmations | 429 | The organization has reached its cap on meal analyses waiting in requires_confirmation. |
organization_suspended | 403 | The organization is suspended; every key is refused. |
organization_pending | 403 | The organization is awaiting activation, so live keys cannot be used yet. Test keys work. |
key_in_query_rejected | 400 | An API key was sent in the URL query string. The request is refused and the key is revoked. |
invalid_image | 422 | The image could not be decoded or breaks an image rule (pixel cap, animation, corrupt data). Also used as error.code when a file is rejected after upload. |
unsupported_media_type | 415 | The request Content-Type, or an uploaded image’s detected format, is not supported. HEIC is not supported in v1. |
image_too_large | 413 | An uploaded image part is larger than 15 MiB. |
file_not_found | 404 | A referenced file_id does not exist in this project and mode, or was deleted. |
file_in_use | 409 | The file is held by a meal analysis that has not finished, so it cannot be deleted yet. |
file_not_ready | 409 | The file’s presigned upload has not been completed (nothing was uploaded, or complete was not called). |
url_fetch_failed | 422 | An image URL could not be fetched: not https, resolves to a private address, redirected to another host, took longer than 10 s, is larger than 15 MiB or is not an image. |
pipeline_not_found | 400 | The requested pipeline is unknown or retired. |
use_reject_instead | 422 | A confirmation with action: edit had an empty items list. |
confirmed_by_must_be_pseudonymous | 422 | confirmed_by looks like an email address or a phone number. |
quantity_text_unsupported_locale | 422 | quantity_text was sent without grams for a non-English locale. |
internal_error | 500 | An unexpected error on our side. Also a workflow failure code. Never billed. |
maintenance | 503 | Intake is paused for maintenance. |
request_too_large | 413 | The request body exceeds its limit (1 MiB for JSON bodies). |
service_unavailable | 503 | A dependency is temporarily unavailable; creates are refused rather than run unmetered. |
Workflow failure codes
These never appear as an HTTP response. They are the error.code of a meal analysis or nutrition
estimate whose status is failed (invalid_image and internal_error can appear in both places).
| Code | Meaning |
|---|---|
pipeline_deprecated | Reserved; not returned in v1. Requests on a deprecated release succeed and carry Deprecation and Sunset headers. |
upstream_unavailable | Workflow failure: the model provider stayed unavailable past the stage deadline. Retryable; never billed. |
upstream_invalid_response | Workflow failure: the model provider kept returning unusable output. |
processing_timeout | Workflow failure: a stage did not finish within its deadline after retries. Never billed. |
queue_timeout | Workflow failure: the resource waited in queued for more than 24 hours (for example its image never became ready). Never billed. |
pipeline_retired | Workflow failure: the pinned release was retired before the workflow finished. A platform fault; never billed. |
Retrying
| Status | Retry? |
|---|---|
400, 401, 403, 404, 413, 415, 422 | No — fix the request first |
409 idempotency_request_in_progress | Yes, after Retry-After, with the same Idempotency-Key |
409 invalid_state, 412 | No — reconcile with current_state, then decide |
429 | Yes, after Retry-After |
500, 503 | Yes, with exponential backoff and the same Idempotency-Key |