Errors & pitfalls
How StitchAPI errors work, the code-to-docs contract, and an index of every coded failure.
When a stitch can't produce a result, it fails with a StitchError — an Error
subclass exported from stitchapi, carrying a human-readable message, an
optional .status (the upstream HTTP status), and .attempts (how many tries
the runtime made before giving up). When the failure came from an HTTP response it
also carries .body (the parsed error payload — an API's { error: "…" }) and
.url (the final request URL, after redirects); both are undefined for
transport/internal errors. Await a stitch and that error is what catch receives —
narrow it with instanceof:
import { , } from 'stitchapi';
const = ({
: 'https://api.example.com',
: '/me',
});
try {
await ();
} catch () {
if ( instanceof ) {
// .body is the parsed error payload; .url is the final request URL.
.(., ., ., ., .);
}
}.body carries the upstream error payload only to the awaited / .safe()
caller — it rides a non-enumerable channel and is never written to a trace
sink, so an { error: "…" } payload can't leak into a JSONL/console log. If a
non-2xx is actually expected control flow (a 404 you fall back from), see
acceptStatus — it returns the body
as a normal result instead of throwing.
If you read the event stream instead of awaiting,
the same failure arrives as an error event carrying
{ name, message, status?, attempts, at } — attempts is how many tries the
runtime made before giving up, at is when it gave up.
Handle failure without throwing
await and .unwrap() throw on failure. When you'd rather branch than wrap the
call in try/catch, .safe() consumes the call and never throws — it resolves
to a discriminated { ok, data, error }, where error is the same StitchError,
or null on success:
import { } from 'stitchapi';
const = ({ : 'https://api.example.com', : '/me' });
const { , } = await .();
if () .(., .);
else .(); // the validated result; error is null here.unwrap() is the explicit throwing twin of .safe() — the value, or a thrown
StitchError, exactly like awaiting the bare call. See
the event stream for the iterate-instead-of-await
view of the same outcomes.
The code-to-docs contract
Each catalog page below owns a stable code (STITCH_VALIDATION,
STITCH_DRIFT, and so on). The code's page slug is its URL — the failure
documented at /errors/<slug> is the page the runtime will deep-link to from a
thrown error. Slugs are an API: once a page is published its slug is never
renamed, only added-and-redirected, so a link a runtime emitted a year ago still
resolves.
These codes are provisional. Today the runtime throws a StitchError
with a message and optional .status — the stable code and url fields
land with the error-taxonomy refactor. The slugs below are already fixed, so
you can rely on the URLs even while the in-runtime codes are still on the
way.
Every coded failure
Debugging one of these with an agent? Connect the docs
MCP and it can pull any error page on demand
— search_docs('STITCH_DRIFT'), search_docs('rate limit') — instead of
guessing from stale training data.
STITCH_VALIDATION
A response failed its output schema — the shape you asked for isn't the shape you got.
STITCH_DRIFT
A response drifted from the schema past the level you allowed, so the stitch refused it.
STITCH_AUTH_WALL
Authentication failed, or a soft 200 login wall was hit and couldn't be refreshed.
STITCH_TIMEOUT
A call exceeded its timeout budget before a response arrived.
STITCH_CIRCUIT_OPEN
The circuit breaker is open after repeated failures and is short-circuiting calls.
STITCH_GRAPHQL
A GraphQL-over-HTTP response came back 200 but carried an errors
array.
RateLimitError
A delegate-backoff stitch surfaced a rate-limit response for an outer gate to back off on, instead of retrying it.