String API
API Reference

Errors

Status codes and error response shapes.

Status codes

StatusMeaning
400Validation error — invalid input, or a path that requires KYC.
401Missing or invalid Authorization header.
402Insufficient account balance.
403Destination domain not enabled for your organization.
429Rate limit exceeded.
502Destination or upstream fetch provider returned an error.
500Internal server error.

One failure class does not appear in that table because it has no fixed status of its own: a terminal origin failure is returned under whatever status the destination itself answered with, including ones you will not otherwise see from us, such as 530.

Error response shape

Most errors return an ErrorResponse object:

{
  "error": "Validation error",
  "message": "URL protocol must be http or https",
  "statusCode": 404,
  "traceId": "…",
  "issues": [{ "field": "url", "message": "URL protocol must be http or https" }]
}
FieldDescription
errorError type identifier.
messageHuman-readable error message.
statusCodeThe status the destination returned, when the request reached it. Not the HTTP status of this response — read that from the response line.
errorTypeMachine-readable failure class. See Failure classes.
traceIdOptional trace identifier.
issuesPer-field validation issues (field, message).
dataThe destination's own response body, on the one failure class where that body is the answer. See Terminal origin failures.
headersThe destination's own response headers. Present exactly when data is.

Reading a failed fetch

Read two things, and do not confuse them:

  • The HTTP status of our response says whose fault it is. 502 means the request failed upstream of us. 500 means we have no destination response to report — either the request never reached the destination (proxy failure, DNS, TLS) or something went wrong on our side.
  • statusCode in the body is what the destination itself returned. It is also on the x-status-code response header, the same header a successful fetch sets to the same value, so one read works either way. It is absent when we never got a response out of the destination — absent means "no response", not 0.
{
  "error": "Request failed",
  "statusCode": 403,
  "message": "Upstream service error"
}

Sent with HTTP 502: the destination answered 403, and we could not return that answer to you.

Note that statusCode can be a success status. A destination that serves an anti-bot challenge or a login wall as a 200 will report statusCode: 200 on a 502 — the response came back, it just was not the content you asked for.

For a 4xx on the terminal-origin path, statusCode is also passed through as our HTTP status. Key off statusCode and errorType, never off our HTTP status alone, which collapses distinct upstream faults onto 502.

A 403 has two unrelated meanings

A body with reason means the destination domain is not enabled for your organization — see Access control & KYC. Anything else is the destination's own 403, passed through.

Failure classes

errorType is present only for failures where retrying the same request is not the remedy. Key off it rather than the status code, which collapses every upstream fault to 502.

errorTypeWhat happenedWhat to do
TargetCertificateErrorThe destination's TLS certificate cannot be verified.See ignoreCertificateErrors.
TooManyRedirectsErrorThe destination's redirect chain does not terminate.The destination has to fix it; do not retry.
TerminalOriginErrorThe destination's own configuration produced the answer.Do not retry. Read data — see Terminal origin failures.

Terminal origin failures

TerminalOriginError marks a response the destination's own configuration decided. A private object, a hostname whose DNS no longer resolves, a certificate the origin never finished installing — no retry, exit rotation or fingerprint reaches a different answer, because none of those is what the destination is objecting to.

Two families produce it today:

  • Object-store denials. A private object, or a signed URL whose signature has expired or was built wrong, answering AccessDenied.
  • Cloudflare origin errors. The edge is up and the origin behind it is not: 10001004, 1013, 1016 and 1018 (DNS and hostname misconfiguration), and 525/526 (the origin's TLS).

This is the one failure class whose shape differs from the rest of this page. Our HTTP status is the destination's own status rather than 502, and the body carries the destination's response verbatim:

{
  "error": "Request failed",
  "statusCode": 403,
  "message": "The destination answered in a way that will not change on retry; its response is included.",
  "errorType": "TerminalOriginError",
  "data": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>AccessDenied</Code><Message>Access Denied</Message></Error>",
  "headers": { "content-type": "application/xml", "server": "AmazonS3" }
}

Sent with HTTP 403 — the destination's status, also on x-status-code. A Cloudflare 1016 arrives the same way under HTTP 530, carrying that error page in data.

So the rule higher up this page — that our HTTP status only tells you whose fault it is — has one exception here, and it is the useful kind: the status is the destination's, and data is its own explanation of why. Read data and act on what the destination said. The remedy is almost always on its side: the object is private, the signed URL expired, the domain no longer resolves.

Always a JSON envelope

Like every error on this page, this one is returned as JSON whatever format you requested, so a format: "raw" request receives this envelope rather than raw bytes.

Common cases

400 — validation

{
  "error": "Validation error",
  "message": "GET requests cannot have a body",
  "issues": [{ "field": "body", "message": "GET requests cannot have a body" }]
}

401 — authentication

{ "error": "Missing or invalid Authorization header" }

402 — insufficient balance

{ "error": "Insufficient balance" }

403 — destination not enabled

{ "reason": "Requests to example.com are not allowed. Contact [email protected] for access." }

See Access control & KYC for the 400 vs 403 distinction.