Under heavy development
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 rateLimit: { delegate: true } that gets a rate-limit response (status in rateLimit.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 rateLimit.on).
  • retryAfterMs — 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).

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 retryAfterMs (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 retryAfterMs — 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 retryAfterMs 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 rateLimit and use retry + throttle.

On this page