Error catalog¶
Every request to the API that fails returns the same JSON error shape, with a stable, machine-readable code identifying what went wrong. This page lists every code the API can return to a partner today, what it means, and what to do about it. It does not cover a failed analysis job: that is a different kind of failure, described at the bottom of this page.
The error envelope¶
A failed request always responds with a JSON body of this shape:
{
"error": {
"code": "malformed_request",
"message": "`upload_handle` is required and must be a non-empty string."
}
}
code is the stable, machine-readable string to branch your handling on;
it will not change once published. message is a human-readable sentence
for logs and debugging; treat its exact wording as informational, not
something to parse or match against.
Retryable vs terminal¶
Each code below is marked either Retryable or Terminal.
A Retryable failure is a transient, server-side condition. The request itself was fine; retrying it later, after the wait or corrective step named in its entry, can succeed with no change to what you sent.
A Terminal failure means something about the request or its credentials needs to change before trying again. Retrying it unchanged will fail the same way every time.
Quick reference¶
| Code | HTTP status | Retryable / Terminal |
|---|---|---|
authentication_error |
401 | Terminal |
malformed_request |
400 | Terminal |
unknown_exercise |
404 | Terminal |
upload_not_found |
404 | Terminal |
upload_spot_expired |
410 | Terminal |
upload_incomplete |
409 | Terminal |
upload_already_claimed |
409 | Terminal |
spots_per_window_exceeded |
429 | Retryable |
unclaimed_bytes_exceeded |
429 | Retryable |
store_unavailable |
503 | Retryable |
storage_stat_unavailable |
503 | Retryable |
catalog_error |
500 | Retryable |
internal_error |
500 | Terminal |
job_not_found |
404 | Terminal |
Codes¶
authentication_error · 401 · Terminal¶
Meaning: The request's credentials were rejected. A missing or
malformed Authorization header, an invalid key, a suspended key, and a
revoked key all produce this exact same code and the same message:
"Invalid, suspended, or revoked API key." The response never tells you
which of these applies.
Action: Confirm the header is present and formed as Authorization:
Bearer <key>. If the key looks correct and is still rejected, it may have
been suspended or revoked; ask the operator to check its status.
malformed_request · 400 · Terminal¶
Meaning: The request body was not valid JSON, was not a JSON object, or was missing a required field.
Action: Fix the request body and resubmit. The message names the
specific problem, for example a missing upload_handle or exercise_id.
unknown_exercise · 404 · Terminal¶
Meaning: The exercise id in the request does not exist in the exercise catalog.
Action: Look up a valid id from the exercise catalog endpoint described in the endpoint reference, then resubmit with a valid id.
upload_not_found · 404 · Terminal¶
Meaning: No upload spot exists for the handle given. This covers two different situations on purpose: a handle nobody ever minted, and a handle that exists but belongs to a different key. Both return this exact same response, so a caller cannot use the response to tell "this handle was never yours" apart from "this handle does not exist."
Action: Check the handle for a copy or transcription error, or mint a fresh upload spot and start over.
upload_spot_expired · 410 · Terminal¶
Meaning: The upload spot existed but its lifetime window has already passed.
Action: Mint a new upload spot and complete the upload within its window this time.
upload_incomplete · 409 · Terminal¶
Meaning: The upload spot exists and is still within its window, but no video bytes have landed on it yet.
Action: Complete the upload to the spot before submitting a job against it.
upload_already_claimed · 409 · Terminal¶
Meaning: This has two distinct outcomes depending on what you send. If the handle was already claimed by a job for the same exercise id you are submitting again, that is not an error at all: the request succeeds with a 201 and returns the original job id unchanged. This code and status are returned only when the handle was already claimed by a job for a different exercise id than the one in the new request.
Action: If you meant to submit the same exercise again, resend the exact same exercise id and expect the original job id back. If you meant a different exercise, mint a new upload spot for it; a handle can only be claimed once.
spots_per_window_exceeded · 429 · Retryable¶
Meaning: This key has already minted the maximum number of upload spots allowed within the current rolling window.
Action: Wait for the window to roll forward and free a spot, then retry minting.
unclaimed_bytes_exceeded · 429 · Retryable¶
Meaning: This key's total unclaimed upload bytes have gone over the cap. See limits and quotas for the current figures.
Action: Submit jobs for outstanding uploads to claim their bytes, or wait for unclaimed spots to expire, then retry minting.
store_unavailable · 503 · Retryable¶
Meaning: A durable backing store the API depends on, the API key
store, the upload-spot store, or the job store, is temporarily
unreachable. This is distinct from storage_stat_unavailable below,
which covers only the video storage layer.
Action: Wait a moment and retry.
storage_stat_unavailable · 503 · Retryable¶
Meaning: A query against the video storage layer failed or timed
out. This is distinct from store_unavailable above, which covers the
key, upload-spot, and job stores rather than video storage.
Action: Wait a moment and retry.
catalog_error · 500 · Retryable¶
Meaning: The exercise catalog could not be read or resolved.
Action: Wait a moment and retry. If it keeps happening, contact the operator at support@fitizens.io.
internal_error · 500 · Terminal¶
Meaning: The server retained the uploaded video but could not confirm that the job record itself was written. This is a rare server-side failure, not a problem with the request you sent.
Action: Do not immediately resubmit with the same handle; it may already be marked claimed. Contact the operator at support@fitizens.io with the approximate time of the request so the job can be reconciled.
job_not_found · 404 · Terminal¶
Meaning: No job exists for the given id, or it exists but belongs to
a different key. As with upload_not_found, both cases return this exact
same response so a foreign job's existence cannot be probed.
Action: Check the job id for a copy or transcription error.
A failed job is not an envelope error¶
A job that reaches its failed state is not reported through this envelope. Polling or retrieving that job still returns a normal 200 response; the failure shows up as a reason recorded on the job's own status payload, not as one of the codes above. See the endpoint reference for where that field lives on the job status response and how to read it.