Timeouts and deadlines
Stop waiting on a model call that hangs, cancel the request with an AbortSignal, and keep retries inside a deadline so serverless functions fail cleanly instead of being killed.
Retries handle calls that fail. Some calls don't fail: they just never answer. A request stuck for five minutes holds a user's spinner, and on a serverless host, it holds the whole request until the host kills it.
Serverless limit · 300s (the host kills the request)
Deck deadline · 240s
A timeout for one call
withTimeout(fn, ms) races fn against a timer. If the timer wins, it rejects with a TimeoutError, which classifyError already treats as retryable.
Rejecting isn't enough on its own: the request would keep running in the background, still costing tokens. So fn gets an , the model call receives it, and the timer aborts it.
withTimeout((signal) => model.invoke(messages, { signal }), 90_000)A deadline for the whole job
Hosts like Vercel give a a hard limit, say 300 seconds. Three calls of 90 seconds each, with retries and waits between them, can pass it. So withRetry gets a : a timestamp. Before waiting, it checks whether the wait would end after the deadline. If so, it throws the real error right away.
Key takeaways
- Give every model call a timeout, and pass an AbortSignal so the request is really canceled.
- A deadline covers the whole job: all attempts and all waits between them.
- Fail before the host's time limit, with a real error, instead of being killed mid-request.
Sign in to run the exercise
Reading is free. Writing code here needs an account so we have somewhere to keep your Gemini key and the +35 XP you are about to earn.