RateLimitError
The delegate-backoff error: a rate-limit response surfaced for an outer gate to back off on, instead of being retried internally.
What you'll see
A stitch with throttle: { delegate: true }
that gets a rate-limit response (status in throttle.on, default [429])
rejects with a RateLimitError instead of retrying. Unlike most failures,
this is not a StitchError — it's its own exported class, so you can catch it
specifically and hand the signal to whatever owns your backoff:
import { , } from 'stitchapi';
const = ({
: 'https://api.example.com',
: '/pages/{id}',
: { : true },
});
try {
await ({ : { : '42' } });
} catch () {
if ( instanceof ) {
.(.); // 429 (the rate-limit status that was hit)
.(.); // ms parsed from Retry-After, or undefined
.(..); // the raw response, for other rate headers
}
}RateLimitError carries:
status— the rate-limit status (e.g.429, or whatever you listed inthrottle.on).retryAfter— the wait the server asked for, parsed fromRetry-After(delta-seconds or an HTTP-date) into milliseconds;undefinedwhen the header is absent or unparseable.response— the rawAdapterResponse, so you can read other rate headers the API sent (X-RateLimit-Remaining, a reset timestamp, and so on).
This is the one entry in the error catalog that is a real
runtime identity rather than a documentation ID: RateLimitError is an
exported class you can instanceof. The STITCH_* entries are page IDs —
those failures all arrive as a plain StitchError with no .code, told
apart by their fields.
Why it happens
You opted into delegate-backoff because an outer gate or circuit — not StitchAPI — owns the rate budget. So when the API returns a rate-limit status, the stitch deliberately does not retry it and does not pace it on the internal throttle: it surfaces the outcome so your gate can apply the backoff, persist it, and decide when to release. This is the mode working as designed, not a new failure — the rate limit reached you on purpose.
How to handle it
- Feed it to your outer gate. Use
retryAfter(falling back to your own default when it'sundefined) to set the gate's penalty window, then let the gate gate the next attempt. That's the whole point of delegating. - Read it off the stream instead. If you consume
the event stream rather than awaiting, the same
outcome arrives as an
errorevent carryingstatusandretryAfter— notry/catchneeded. - Branch without throwing.
.safe()returns the failure as aStitchErrorwhose.statusis the rate-limit status and whose.causeis the originalRateLimitError(soretryAfteris still reachable via the cause). - Reconsider whether to delegate. If nothing outside StitchAPI actually
owns the backoff, you probably want internal handling instead — drop
throttle.delegateand use retry + throttle.
Stuck, or debugging with an agent? Ask the docs
MCP — search_docs('RateLimitError') pulls
this page and the related guides straight into context.