Release candidate — 1.0.0-rc.6
StitchAPI

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 in throttle.on).
  • retryAfter — the wait the server asked for, parsed from Retry-After (delta-seconds or an HTTP-date) into milliseconds; undefined when the header is absent or unparseable.
  • response — the raw AdapterResponse, 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's undefined) 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 error event carrying status and retryAfter — no try/catch needed.
  • Branch without throwing. .safe() returns the failure as a StitchError whose .status is the rate-limit status and whose .cause is the original RateLimitError (so retryAfter is 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.delegate and use retry + throttle.

Stuck, or debugging with an agent? Ask the docs MCPsearch_docs('RateLimitError') pulls this page and the related guides straight into context.

On this page