upAPI Docs

Errors

One error envelope, one code vocabulary, and what each code means.

View as Markdown

Every failure — bad input, a missing operation, a rate limit, a dead upstream — comes back in the same shape:

{
  "error": {
    "code": "INVALID_INPUT",
    "message": "language: String must contain at most 30 character(s)"
  }
}

code is a stable, machine-readable identifier: branch on it. message is written for a human reading a log and may change at any time, so do not parse it. For a marketplace operation the HTTP status matches the code, which means a classified failure reaches you as the status it deserves rather than a blanket 502. Hosted functions are the exception, in both the status and the shape — see below.

Every response, successful or not, also carries an x-request-id header. Quote it when reporting a problem — it is how we find your exact call.

Codes

CodeHTTPMeaning
CALLER_PROXY_AUTH_REJECTED400The proxy you supplied (proxyUrl or x-proxy-url) refused our connection with HTTP 407. Check its credentials and whether your provider allow-lists the connecting IP. Distinct from PROXY_UNAVAILABLE, which is about our own pool.
INVALID_INPUT400The input failed the operation's schema validation.
INVALID_JSON400The request body was not parseable JSON.
INVALID_PATH400The request path is not a valid operation route.
MEMORY_EXCEEDED400A hosted function exceeded the memory allowed by your plan. Delivered as a 422, see below.
MISSING_FUNCTION_ID400The request did not identify an operation to run.
AUTH_EXPIRED401The supplied upstream credentials or session are no longer valid.
AUTH_REQUIRED401This operation requires credentials that were not supplied.
UNAUTHORIZED401The x-api-key header is missing, unknown, or disabled.
FORBIDDEN403Authenticated, but your plan or organization may not perform this action.
NETWORK_DENIED403An outbound request was blocked by the SSRF guard (private, link-local, or loopback address).
NOT_FOUND404No operation is published at this slug for your organization.
CHECKPOINT_REQUIRED409The upstream site stopped the operation at a security checkpoint a person must clear (an in-app approval, an authenticator prompt, or an identity check). Retrying will not help.
CLIENT_REJECTED409The upstream site rejected our client itself — a request signature, a device attestation, or an app-integrity check — rather than your input. Retrying, waiting, or routing through a different network will not change the answer.
IDEMPOTENCY_IN_FLIGHT409The first request with this Idempotency-Key is still running. Retry to receive its result.
IDEMPOTENCY_KEY_REUSED409This Idempotency-Key was already used for a different request. Use a new key.
NO_MOBILE_EXIT409You asked a proxy lease for a mobile-carrier exit and none of the exits probed was cellular. Retrying with the same inputs gives the same answer: try a different mobileCountry, or drop the requirement and accept a residential exit.
PHONE_VERIFICATION_REQUIRED409The upstream site stopped the operation and demanded a phone number. A person with a number is the only thing that clears it, so retrying will not help.
QUOTA_EXCEEDED429A budget is spent: the month's weighted units, a trial's unit allowance, or your plan's hosted-function compute seconds.
RATE_LIMITED429You exceeded the per-minute burst limit. It refills on its own within 60s.
SERVICE_BUSY429Execution capacity is momentarily saturated. Retry with backoff.
INTERNAL_ERROR500An unexpected failure on our side. Report it with the x-request-id.
MISSING_HANDLER500The operation is registered without a runnable handler.
OPERATION_ERROR500The operation raised an unclassified error while running.
NOT_IMPLEMENTED501The operation exists but this code path is not implemented.
CAPTCHA_FAILED502The upstream site presented a challenge that could not be solved.
INVALID_OUTPUT502The operation's result did not match its declared output schema.
INVOCATION_FAILED502The operation could not be dispatched to a worker.
PROXY_UNAVAILABLE502No healthy proxy was available for an operation that requires one.
UPSTREAM_ERROR502The third-party service returned an error or blocked the request.
UPSTREAM_INVALID_RESPONSE502The third-party service answered in a shape we could not parse.
UPSTREAM_UNREACHABLE502The third-party service could not be contacted at all.
AUTH_UNAVAILABLE503A session surface could not reach our authentication infrastructure. Keyed calls see SERVICE_UNAVAILABLE for the same outage — retry shortly.
SERVICE_UNAVAILABLE503A dependency of the platform is currently unavailable.
OPERATION_TIMEOUT504The operation exceeded its own timeout budget.
UPSTREAM_TIMEOUT504The third-party service the operation calls did not answer in time.

Semantics worth knowing

These are the cases where the obvious reading of a status is wrong.

404 does not distinguish "missing" from "not yours"

An unknown slug and an operation that exists but is not published to your organization both return 404 NOT_FOUND, with the same message. This is deliberate: probing the gateway must not reveal which private operations exist. If you expected an operation to work and get a 404, check the marketplace listing rather than assuming a typo.

Hosted-function failures arrive as a 4xx, whatever the code says

Cloudflare replaces any origin 5xx body with its own opaque error page, which would destroy the envelope. A hosted function's failures are attributable to your code, so the gateway deliberately returns them with an edge-safe status instead of the code's canonical one:

CodeTable saysYou receive
OPERATION_TIMEOUT504408
SERVICE_BUSY429429
MEMORY_EXCEEDED, INVALID_OUTPUT, OPERATION_ERROR, …400 / 502422

The canonical status is preserved in an X-Upapi-Execution-Status header if you need it. This remapping applies only to hosted functions; marketplace operations return the status in the table above.

Hosted functions also use a different envelope

A failure raised inside the isolate comes back as {"error": "MEMORY_EXCEEDED", "message": "…"} — error is a string, not the { code, message } object. Read both shapes if you call hosted functions, exactly as the gateway itself does.

MEMORY_EXCEEDED is a client error, and always a 422

Exceeding the memory your plan allows is treated as a problem with the request — you asked for more than you bought — so it is a client error rather than a 500. Only a hosted function can raise it, so on the wire it is always a 422 by the remapping above, never the 400 the code table lists. Retrying the same call unchanged will fail the same way; either shrink the workload or move up a plan.

AUTH_UNAVAILABLE is a 503, not a 401 — on session surfaces

This code means our authentication infrastructure could not be reached, so your credentials were never checked. It is an outage signal, not a credential signal. Retry with backoff; do not rotate keys and do not surface "invalid credentials" to your users.

It is emitted by the surfaces that authenticate a session: the dashboard, the marketplace try-it runner, and the hosted MCP endpoint. A call authenticated with an API key gets 503 SERVICE_UNAVAILABLE for the same outage — a different code, the same status and the same response. Handle both.

429 arrives with a reason

Three codes share the 429 status, and the code — not the status, and not the size of Retry-After — is what tells you whether waiting will help:

  • RATE_LIMITED — you exceeded the per-minute burst limit. This is the one 429 that clears itself: the window refills within 60 seconds and nothing has been spent. Wait and retry.
  • QUOTA_EXCEEDED — a budget is spent. That is the month's weighted units, a trial's included units, or your plan's compute seconds for hosted functions. Retrying is pointless until the budget refills; Retry-After, where present, points at the moment it actually does — the next UTC month, or the end of your trial. Upgrading is the other fix.
  • SERVICE_BUSY — execution capacity is momentarily saturated. Retry with backoff.

This split changed

A monthly-quota or trial-cap rejection used to be RATE_LIMITED as well, leaving Retry-After's magnitude as the only clue. If your client branches on RATE_LIMITED alone, it now retries only the burst window — which is what it should always have done. Handle QUOTA_EXCEEDED as a stop.

See Rate limits for the headers that tell you which is which before you hit them.

The two 409s are about idempotency, and only one is your mistake

Both are raised by Idempotency-Key handling:

  • IDEMPOTENCY_KEY_REUSED — you sent a key that a different request already used. Nothing ran. Two requests may not share a key; generate a new one.
  • IDEMPOTENCY_IN_FLIGHT — you sent a key whose first request is still running. Nothing ran twice, either. Wait for Retry-After (one second) and send the same request again; you will get the first one's stored response.

The two proxy codes name whose proxy failed

Both mean a proxy refused to carry the request, and the fix is on opposite sides:

  • PROXY_UNAVAILABLE (502) — our exit pool. No proxy was available, or the one we leased stopped working mid-call. Nothing about your request caused it and nothing in your request can fix it. Retry: a later call leases a different exit.
  • CALLER_PROXY_AUTH_REJECTED (400) — the proxy you supplied, through an operation's proxyUrl, the x-proxy-url header, or your organization's configured proxy list, answered our connection with HTTP 407. Check its credentials, and whether your provider requires allow-listing the IP that connects to it. Retrying unchanged fails identically.

5xx codes name the layer that failed

UPSTREAM_* codes mean the third-party service an operation wraps misbehaved — the platform worked. OPERATION_ERROR and INVALID_OUTPUT point at the operation itself. INTERNAL_ERROR is ours; report it with the x-request-id.

Retrying

Retry SERVICE_BUSY, SERVICE_UNAVAILABLE, AUTH_UNAVAILABLE, IDEMPOTENCY_IN_FLIGHT, the UPSTREAM_* family and RATE_LIMITED — with exponential backoff, and always respecting Retry-After when it is present.

Do not retry INVALID_INPUT, INVALID_JSON, INVALID_PATH, NOT_FOUND, UNAUTHORIZED, FORBIDDEN, MEMORY_EXCEEDED, IDEMPOTENCY_KEY_REUSED, CALLER_PROXY_AUTH_REJECTED or QUOTA_EXCEEDED without changing something first; the same request will fail identically.

Because an operation is a POST with real upstream side effects, a retry that is safe is not automatically a retry that is free — send an Idempotency-Key so a retried call replays the first result instead of running and billing a second time.

The TypeScript SDK implements exactly this policy, idempotency key included.

One envelope, one exception

A request body over 5 MB is rejected at the edge, before it reaches the gateway, with 413 and the code PAYLOAD_TOO_LARGE. That code is not part of the operation vocabulary above — treat any oversized-body handling as a separate branch.