sindresorhus/got · error · TypeError
The `beforeError` hook must return an Error instance. Receiv
Error message
The `beforeError` hook must return an Error instance. Received ${is.string(error) ? 'string' : String(typeof error)}. What it means
Thrown at source/core/index.ts:2512 inside the `_error` method's beforeError hook loop. Each beforeError hook is awaited and is expected to return a value that IS an Error (typically the same error, possibly enriched with context, or a wrapped RequestError). If a hook returns undefined, a string, an object, or any non-Error value, got throws this TypeError rather than continuing with a non-error and producing a confusing rejection later. The message reports the runtime type so you can spot the mistake quickly.
Source
Thrown at source/core/index.ts:2512
throw error;
}
}
private async _error(error: RequestError): Promise<void> {
try {
// Skip calling hooks for HTTP errors when throwHttpErrors is false (Promise API only).
// See https://github.com/sindresorhus/got/issues/2103
if (this.options && (!(error instanceof HTTPError) || this.options.throwHttpErrors)) {
const hooks = this.options.hooks.beforeError;
if (hooks.length > 0) {
for (const hook of hooks) {
// eslint-disable-next-line no-await-in-loop
error = await hook(error) as RequestError;
// Validate hook return value
if (!(error instanceof Error)) {
throw new TypeError(`The \`beforeError\` hook must return an Error instance. Received ${is.string(error) ? 'string' : String(typeof error)}.`);
}
}
// Mark this error as processed by hooks so _destroy preserves custom error types.
// Only mark non-RequestError errors, since RequestErrors are already preserved
// by the instanceof check in _destroy (line 642).
if (!(error instanceof RequestError)) {
errorsProcessedByHooks.add(error);
}
}
}
} catch (error_: unknown) {
const normalizedError = normalizeError(error_);
error = new RequestError(normalizedError.message, normalizedError, this);
}
// Publish error event
publishError({View on GitHub (pinned to e3924aa1e5)
Solutions
- Always return the (possibly transformed) error from beforeError hooks — `return error;` at minimum.
- If you wrap the error, instantiate a real Error subclass: `return new RequestError('...', error, options)` or `return new MyError(message, { cause: error })`.
- Add a TypeScript return type annotation `: Error` on the hook so the compiler flags missing returns.
Example fix
// before
hooks: {
beforeError: [error => { log(error); /* missing return */ }]
}
// after — always return an Error
hooks: {
beforeError: [error => { log(error); return error; }]
}
// wrapping
hooks: {
beforeError: [error => new RequestError(`upstream failed: ${error.message}`, error, error.request)]
} Defensive patterns
Strategy: validation
Validate before calling
// Wrap beforeError hooks to guarantee they return an Error.
function wrapBeforeError(hook) {
return async (error) => {
const next = await hook(error);
if (!(next instanceof Error)) {
throw new TypeError(`beforeError hook must return an Error instance; received ${typeof next}`);
}
return next;
};
}
options.hooks.beforeError = (options.hooks.beforeError ?? []).map(wrapBeforeError); Type guard
function isErrorInstance(v: unknown): v is Error {
return v instanceof Error;
} Try / catch
try {
await got(url, { hooks: { beforeError: [hook] } });
} catch (error) {
if (error instanceof TypeError && /beforeError.*hook must return an Error/.test(error.message)) {
throw new Error('beforeError hook contract violation — make sure the hook returns an Error', { cause: error });
}
throw error;
} Prevention
- Always `return error;` at the end of beforeError hooks, even on logging-only paths.
- Wrap into a real Error subclass (RequestError, or your own) — never a plain object or string.
- Annotate hook types as `(error: RequestError) => RequestError | Promise<RequestError>` so TS catches missing returns.
When it happens
Trigger: A beforeError hook that logs and forgets to return the error; returns a string message; returns a plain `{ message }` object; returns `null` to swallow the error.
Common situations: Logging/metrics hooks that forget `return error`; refactoring a hook to branch and missing a return on one path; converting errors to a domain-specific shape using a plain object instead of an Error subclass.
Related errors
- The `afterResponse` hook returned an invalid value
- beforeCache hook must return false or undefined. To modify t
- ERR_RETRYING
- A retry listener has been attached already.
- The reassigned stream body must be readable. Ensure you prov
AI-assisted analysis of sindresorhus/got@e3924aa1e5 (2026-08-03).
Data as JSON: /data/errors/e180894a83f248b8.json.
Report an issue: GitHub.