Errors
FLORA returns conventional HTTP status codes and a structured error body on every non-2xx response.
Error shape
Section titled “Error shape”{ "error": { "code": "input_validation_error", "message": "prompt: prompt is required.", "fields": [{ "field": "prompt", "message": "prompt is required." }] }}For validation errors, fields lists each rejected field when field-level details are available.
Every response also includes a request-id header. Always capture it — support needs it to investigate.
curl -i https://app.flora.ai/api/v1/techniques/thumbnail-v3/runs \ -H "Authorization: Bearer $FLORA_API_KEY" \ -H "Content-Type: application/json" \ -d '{"inputs": [], "mode": "async"}'
# < HTTP/2 400# < request-id: req_abc123...# < content-type: application/json## {"error":{"code":"input_validation_error","message":"..."}}All error codes
Section titled “All error codes”400 invalid_json
Section titled “400 invalid_json”The request body wasn’t valid JSON.
Fix: Validate JSON before sending. If using a SDK, this almost always indicates a serialization bug in your code — not a server issue.
400 input_validation_error
Section titled “400 input_validation_error”The request body parsed, but a field is missing, has the wrong type, uses the wrong public ID prefix, or doesn’t match the Technique’s input schema.
Fix: Use the fields array and message to correct the payload. Common fixes:
- Use public API IDs with the expected prefixes:
ws_for workspaces,prj_for projects,tech_for techniques,run_for runs. - For generations, use
type: "image" | "video" | "audio" | "text"; don’t uset2i,i2v, ortext-to-imageastype. - Pass
modelas a model endpoint ID string fromGET /api/v1/modelsor MCPlist_models. - For asset uploads, pass
sourceas a string:"signed-url"or an allowlisted HTTPS URL. - For technique runs by slug, call
GET /api/v1/techniques/{slug}first and match each inputidandtypeexactly.
import { Flora, FloraError } from "@flora-ai/flora"
// The API error envelope rides on `err.body`const errorCode = (err: FloraError) => (err.body as { error?: { code?: string } } | undefined)?.error?.code
try { await client.techniques.runs.create({ techniqueId: "thumbnail-v3", inputs: [], mode: "async", })} catch (err) { if (err instanceof Flora.BadRequestError && errorCode(err) === "input_validation_error") { // Re-fetch the schema and rebuild inputs }}401 unauthorized
Section titled “401 unauthorized”No Authorization header was sent.
Fix: Include Authorization: Bearer ak_XXXX on every request.
401 invalid_api_key
Section titled “401 invalid_api_key”The API key is malformed, revoked, or doesn’t exist.
Fix: Check the key value (no whitespace, no Bearer prefix duplicated, correct env var). If the key was revoked, create a new one in Settings → API Keys.
401 invalid_token (MCP only)
Section titled “401 invalid_token (MCP only)”The OAuth token expired and couldn’t refresh, or was revoked from FLORA → Connected apps.
Fix: Reconnect the MCP client. See MCP troubleshooting.
402 insufficient_credits
Section titled “402 insufficient_credits”The workspace balance (USD) is too low to start the run.
Fix: The workspace balance is a limit of its current plan; see About FLORA credits. The charged_cost from previous completed runs and the run_cost on GET /techniques/{slug} show your burn rate.
403 forbidden
Section titled “403 forbidden”The key is valid but lacks permission for this operation. Common cases:
- Creating a Project with a read-only key.
- Accessing a workspace your account was removed from.
- Calling a Technique that was archived.
Fix: Check the FLORA app to confirm the user/key role. If the role looks right, contact support with the request-id.
403 paid_plan_required
Section titled “403 paid_plan_required”The operation needs a feature the workspace plan does not include — for example sharing a project for editing (access: "edit" on POST /projects/{project_id}/share).
Fix: Use the variant the workspace plan allows (share for viewing instead), or ask a workspace administrator about the plan. See About FLORA credits.
404 not_found
Section titled “404 not_found”The resource ID, Technique slug, or run ID doesn’t exist (or doesn’t belong to your workspace).
Fix:
- For Techniques: verify the slug. The URL
https://app.flora.ai/techniques/{slug}is authoritative. - For runs: the run ID was created in a different workspace, or you typo’d it.
- For assets/projects: confirm the ID by listing the parent collection first.
409 conflict
Section titled “409 conflict”The resource is in a state that doesn’t allow this operation. Examples: completing an asset that’s already complete, retrying an asset that didn’t fail.
Fix: Inspect the resource first (GET /assets/{id}) and only call the action if the current state allows it.
409 brand_unpublished
Section titled “409 brand_unpublished”The brand exists and you can read it, but nothing is published yet. Agents read published releases only, never drafts. The message names the brand, its review counts and the page where a person publishes it.
Fix: Relay the message to the user and retry after they publish. Do not substitute another brand without asking.
422 idempotency_conflict
Section titled “422 idempotency_conflict”You sent the same idempotency_key with a different request body. Idempotency keys must be paired with identical bodies.
Fix: Either reuse the original body (and get the cached response) or choose a new idempotency key. See Idempotency.
429 rate_limited
Section titled “429 rate_limited”You’ve exceeded the per-workspace request rate.
Fix:
- Back off with exponential delay before retrying.
- Reduce parallelism in batch jobs.
- The
retry-afterresponse header (when present) is the recommended wait in seconds.
const retryAfter = parseInt(err.rawResponse?.headers.get("retry-after") ?? "5", 10)await new Promise((r) => setTimeout(r, retryAfter * 1000))// retry500 internal_error
Section titled “500 internal_error”Something broke on our side. The request didn’t complete.
Fix: Retry with backoff. If it persists, send the request-id to support.
503 service_unavailable
Section titled “503 service_unavailable”The API is overloaded or down for maintenance.
Fix: Retry with exponential backoff. Check our status page if reachable.
Run-level errors
Section titled “Run-level errors”A run that starts successfully can still fail during execution. The HTTP request returns 200, but the run status becomes failed:
{ "run_id": "run_abc...", "status": "failed", "error_code": "model_timeout", "error_message": "Underlying model exceeded the 60s budget"}Common error_code values:
| Code | Meaning |
|---|---|
model_timeout | The model didn’t return in time. Usually transient — retry. |
safety_blocked | The model refused the request on safety grounds. Adjust prompts. |
input_unreachable | A URL input (image/video) couldn’t be fetched. Check the URL. |
internal_error | Generic execution failure. Retry; if persistent, contact support. |
Always handle status === 'failed' when polling — the run won’t auto-retry.
Recovery patterns
Section titled “Recovery patterns”Retry on transient errors
Section titled “Retry on transient errors”async function withRetry<T>(fn: () => Promise<T>, max = 3): Promise<T> { let lastErr: unknown for (let attempt = 0; attempt < max; attempt++) { try { return await fn() } catch (err) { if (err instanceof FloraError) { if ([429, 500, 503].includes(err.statusCode ?? 0)) { lastErr = err const delay = Math.min(1000 * 2 ** attempt, 30_000) await new Promise((r) => setTimeout(r, delay)) continue } } throw err } } throw lastErr}Don’t retry user errors
Section titled “Don’t retry user errors”400, 401, 403, 404, 409, 422 are caller mistakes. Retrying them produces the same error — surface them to the user/log instead.
Contacting support
Section titled “Contacting support”When you open a ticket, include:
- The
request-idfrom response headers (or the failing run’srun_id). - The endpoint and HTTP method.
- The error
codeandmessage. - Approximate UTC timestamp.
- Your workspace ID.
The more of these we have, the faster we can diagnose. Without request-id we usually can’t find the request at all.
Related
Section titled “Related”- Authentication — 401 errors and key management.
- Idempotency — safe retry semantics.