Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Completed requests leak listeners on caller-provided AbortSignals

Open
#203 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
74/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
typescript
Domain
api, backend

Research direction

Start in src/core.ts at fetchWithTimeout(), especially lines 549-550, and trace how requests settle across success, error, timeout, and retry paths. Add coverage for listener cleanup on each path and verify that reusing one caller-provided AbortSignal leaves no listeners after completed requests.

Written by the indexing model from the issue text.

Description

Description

Every request made with a caller-provided AbortSignal permanently adds an abort listener to that signal.

At src/core.ts:549-550, fetchWithTimeout() calls:

if (signal) signal.addEventListener('abort', () => controller.abort());

The listener is never removed after the request settles. Reusing one signal across a batch therefore retains one internal AbortController closure per completed request.

Reproduction
import { getEventListeners } from 'node:events';
import Browserbase from '@browserbasehq/sdk';

const controller = new AbortController();
const client = new Browserbase({
  apiKey: 'test',
  maxRetries: 0,
  fetch: async () =>
    new Response('{}', {
      status: 200,
      headers: { 'content-type': 'application/json' },
    }),
});

for (let i = 0; i < 12; i++) {
  await client.get('/ok', { signal: controller.signal });
}

console.log(getEventListeners(controller.signal, 'abort').length); // 12
Expected behavior

A completed request removes its abort forwarding listener, leaving zero listeners after the loop.

Actual behavior

All 12 listeners remain attached. Retries add additional listeners because each attempt creates another controller.

Why this matters

Long-lived applications commonly share a signal across a batch of requests. Listener accumulation retains completed request state and can produce unbounded memory growth for large batches. The forwarding callback should be registered once/removed in cleanup (or use an equivalent combined-signal mechanism), with coverage for success, error, timeout, and retry paths.

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

Dominant language
TypeScript
Stars
64
Forks
17
Avg merge
13m
Merged PRs (30d)
4

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from browserbase/sdk-node

All issues in browserbase/sdk-node

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.