Skip to content

AbortSignal is ignored while waiting for retry backoff #205

Description

@abhinavkr26104

Description

Aborting a request while it is waiting to retry does not interrupt the retry delay. The SDK waits for the full Retry-After/backoff period before it notices the signal is aborted.

retryRequest() uses an unconditional await sleep(timeoutMillis) at src/core.ts:595-630. The caller's options.signal is only checked when makeRequest() starts the next attempt.

Reproduction

import Browserbase from '@browserbasehq/sdk';

let attempts = 0;
const client = new Browserbase({
  apiKey: 'test',
  maxRetries: 1,
  fetch: async () => {
    attempts++;
    return new Response('{}', {
      status: attempts === 1 ? 429 : 200,
      headers: {
        'content-type': 'application/json',
        'retry-after-ms': '500',
      },
    });
  },
});

const controller = new AbortController();
const started = Date.now();
const request = client.get('/retry', { signal: controller.signal });
setTimeout(() => controller.abort(), 20);

await request.catch((error) => {
  console.log(error.constructor.name); // APIUserAbortError
  console.log(Date.now() - started);    // ~500 ms, not ~20 ms
});

Expected behavior

The retry wait is abortable, so the promise rejects with APIUserAbortError promptly after about 20 ms.

Actual behavior

The promise does not reject until the complete 500 ms retry delay has elapsed. A long server-provided Retry-After value makes cancellation ineffective for that entire period.

Why this matters

Abort signals are used to release work when requests, jobs, or incoming HTTP connections are canceled. Keeping canceled operations pending through retry delays wastes resources and makes shutdown/cancellation latency unpredictable. The delay should race against the signal and clean up its listener/timer on either outcome.

Tested against @browserbasehq/sdk 2.18.0 / current main (b781bd7).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions