Errors
The Layer REST API uses conventional HTTP status codes and returns a consistent, machine-readable error body following RFC 7807 Problem Details.
Error shape
Section titled “Error shape”Errors are returned with the application/problem+json content type and this shape:
{ "type": "https://api.layer.ai/errors/WORKSPACE_NOT_FOUND", "title": "Workspace not found", "status": 404, "detail": "Workspace 2489e9ad-… doesn't exist or you're not a member.", "error_code": "WORKSPACE_NOT_FOUND"}error_code: the machine-readable error code. Branch on this, not on the human-readabletitleordetail.type: a stable URI ending in the same code.status: mirrors the HTTP status.detail: a human-readable explanation. For4xxerrors this is specific; for5xxerrors it’s scrubbed to a generic message.
Error codes
Section titled “Error codes”| Code | Status | Meaning |
|---|---|---|
UNAUTHENTICATED |
401 | Missing or invalid bearer token. See Authentication. |
FORBIDDEN |
403 | The authenticated user lacks permission for this action or workspace. |
INSUFFICIENT_BALANCE |
402 | Not enough Creative Units. See Creative Units & billing. |
RATE_LIMITED |
429 | Too many requests. See Rate limits. |
WORKSPACE_NOT_FOUND |
404 | Workspace doesn’t exist or you’re not a member. |
MODEL_NOT_FOUND |
404 | Model not found or not available in this workspace. |
WORKFLOW_NOT_FOUND |
404 | Workflow not found or not linked to this workspace. |
INFERENCE_NOT_FOUND |
404 | Inference (forge) run doesn’t exist. |
RUN_NOT_FOUND |
404 | Workflow run doesn’t exist. |
INVALID_INPUTS |
422 | Malformed parameters, UUIDs, or an invalid pagination cursor. |
UPLOAD_FAILED |
502 | A file could not be downloaded or stored. |
INTERNAL_ERROR |
500 | An unexpected server error. Retry later; contact support if it persists. |
Asynchronous failures
Section titled “Asynchronous failures”Generation is asynchronous, and not every failure surfaces as an HTTP error. In particular, your Creative Unit balance is not checked when you submit a run: an underfunded run is accepted with 202 and later reaches a terminal failed/failure state carrying error_code: INSUFFICIENT_BALANCE.
Refused generations
Section titled “Refused generations”Guardrails and provider content policies judge a generation after it is accepted, so a refusal never comes back as a 4xx. The run fails with one of these error_code values, and carries a refusal object that says why:
| Code | Meaning |
|---|---|
INPUT_GUARDRAIL_VIOLATION |
A guardrail your workspace or project set blocked the request. Nothing was sent to the provider. |
CONTENT_POLICY_VIOLATION |
The model provider refused the request under its content policy. |
PROMPT_INTELLECTUAL_PROPERTY |
The model provider refused the request because it asked for protected intellectual property. |
NO_MEDIA_GENERATED |
The provider returned no output and gave no reason. This is usually a silent refusal of the prompt. |
{ "inference_id": "6f1c…", "status": "failed", "error": "This request was blocked by your workspace's “No Nintendo IP” guardrail. The prompt names Mario.", "error_code": "INPUT_GUARDRAIL_VIOLATION", "refusal": { "inference_id": "6f1c…", "code": "INPUT_GUARDRAIL_VIOLATION", "guardrails": [ { "guardrail_id": "0b6e…", "name": "No Nintendo IP", "category": "custom", "reason": "The prompt names Mario." } ] }}guardrails: forINPUT_GUARDRAIL_VIOLATION, every guardrail that blocked the request, with its id, its name, the reason it gave, and itscategory:copyright_infringement,ip_trademark_infringementorartist_style_mimicryfor the guardrails Layer ships,customfor a rule your workspace wrote. Empty for every other code.
refusal is null on every other outcome. A workflow run that a refused generation stopped fails with BlueprintInvalidRuntimeValueError and lists every refused generation in refusals, oldest first, in the same shape. Webhook deliveries carry the same fields, since their data is the matching GET body.
Resubmitting the same inputs is refused the same way. Change the prompt or the reference images, or ask a workspace admin about the guardrail it names.
Handling errors
Section titled “Handling errors”- Retry
429after theRetry-Afterinterval, and5xxwith exponential backoff. - Do not blindly retry
4xxerrors other than429. Fix the request first. - A retry of an execute request that actually succeeded server-side creates a duplicate run unless you sent an idempotency key with it. Send one on every submit, or prefer polling an existing run over re-submitting.