sindresorhus/got · error · Error
A retry listener has been attached already.
Error message
A retry listener has been attached already.
What it means
Thrown at source/core/index.ts:384 from a `newListener` event handler on the Request stream. The Request reserves the `retry` event for its own internal use — the promise wrapper (as-promise) attaches a single retry listener to drive makeRequest. If a second listener for `retry` is added, got throws immediately because the retry orchestration assumes exactly one consumer; duplicate listeners would cause duplicate retry attempts or double-makeRequest calls.
Source
Thrown at source/core/index.ts:384
for (const [header, value] of Object.entries(source.headers)) {
const normalizedHeader = header.toLowerCase();
if (omittedPipedHeaders.has(normalizedHeader) || connectionListedHeaders.has(normalizedHeader)) {
continue;
}
if (!this.options.shouldCopyPipedHeader(normalizedHeader)) {
continue;
}
this.options.setPipedHeader(normalizedHeader, value);
}
}
});
this.on('newListener', event => {
if (event === 'retry' && this.listenerCount('retry') > 0) {
throw new Error('A retry listener has been attached already.');
}
});
try {
this.options = new Options(url, options, defaults);
if (!this.options.url) {
if (this.options.prefixUrl === '') {
throw new TypeError('Missing `url` property');
}
this.options.url = '';
}
this.requestUrl = this.options.url as URL;
// Publish request creation event
publishRequestCreate({View on GitHub (pinned to e3924aa1e5)
Solutions
- Do not attach your own `retry` listener — use the `retry` option (retry.limit, retry.statusCodes, retry.methods) and the `afterResponse` retry callback instead.
- If you proxy events between emitters, exclude `retry` from the forwarded event list (got's own proxiedRequestEvents at as-promise/index.ts:30 is the reference list).
- Use the hooks API (hooks.beforeRetry, hooks.afterResponse) for any retry-side logic you need.
Example fix
// before
const stream = got.stream('https://api');
stream.on('retry', (count, error) => console.log('retrying', count));
// after — use the retry hook instead
await got('https://api', {
retry: { limit: 3 },
hooks: { beforeRetry: [(options, error, retryCount) => console.log('retrying', retryCount)] }
}); Defensive patterns
Strategy: validation
Validate before calling
// Ensure you never attach your own 'retry' listener. Use hooks instead.
const forbidden = ['retry'];
function assertNoInternalListeners(request, event) {
if (forbidden.includes(event)) {
throw new Error(`Do not attach '${event}' on a got Request — use the retry option or hooks.beforeRetry.`);
}
} Try / catch
try {
request.on('retry', handler);
} catch (error) {
if (error instanceof Error && /retry listener has been attached already/.test(error.message)) {
throw new Error('Use hooks.beforeRetry or the retry option instead of request.on("retry", ...)', { cause: error });
}
throw error;
} Prevention
- Never call request.on('retry', ...); configure retry via the `retry` option and hooks.beforeRetry.
- When proxying events between emitters, exclude `retry` (got's proxiedRequestEvents list is the reference).
- Use the promise API for retry-driven flows.
When it happens
Trigger: Calling `request.on('retry', ...)` yourself on a Request object that got already manages, or attaching two retry listeners. Most commonly hit by users of the stream API (got.stream) who try to hook retry manually, or by code that forwards events from one request to another and inadvertently forwards `retry` too.
Common situations: Custom event-forwarding wrappers, proxying all events from an inner request to an outer emitter (the library itself uses a guarded proxiedRequestEvents list that deliberately omits `retry`), or older tutorials that predate the promise-based retry API advising `.on('retry', ...)`.
Related errors
- ERR_RETRYING
- The `afterResponse` hook returned an invalid value
- The reassigned stream body must be readable. Ensure you prov
- ECONNRESET
- beforeCache hook must return false or undefined. To modify t
AI-assisted analysis of sindresorhus/got@e3924aa1e5 (2026-08-03).
Data as JSON: /data/errors/11de973a30be7905.json.
Report an issue: GitHub.