{"id":"e180894a83f248b8","repo":"sindresorhus/got","slug":"the-beforeerror-hook-must-return-an-error-instan","errorCode":null,"errorMessage":"The `beforeError` hook must return an Error instance. Received ${is.string(error) ? 'string' : String(typeof error)}.","messagePattern":"The `beforeError` hook must return an Error instance\\. Received (.+?)\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"source/core/index.ts","lineNumber":2512,"sourceCode":"\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate async _error(error: RequestError): Promise<void> {\n\t\ttry {\n\t\t\t// Skip calling hooks for HTTP errors when throwHttpErrors is false (Promise API only).\n\t\t\t// See https://github.com/sindresorhus/got/issues/2103\n\t\t\tif (this.options && (!(error instanceof HTTPError) || this.options.throwHttpErrors)) {\n\t\t\t\tconst hooks = this.options.hooks.beforeError;\n\t\t\t\tif (hooks.length > 0) {\n\t\t\t\t\tfor (const hook of hooks) {\n\t\t\t\t\t\t// eslint-disable-next-line no-await-in-loop\n\t\t\t\t\t\terror = await hook(error) as RequestError;\n\n\t\t\t\t\t\t// Validate hook return value\n\t\t\t\t\t\tif (!(error instanceof Error)) {\n\t\t\t\t\t\t\tthrow new TypeError(`The \\`beforeError\\` hook must return an Error instance. Received ${is.string(error) ? 'string' : String(typeof error)}.`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Mark this error as processed by hooks so _destroy preserves custom error types.\n\t\t\t\t\t// Only mark non-RequestError errors, since RequestErrors are already preserved\n\t\t\t\t\t// by the instanceof check in _destroy (line 642).\n\t\t\t\t\tif (!(error instanceof RequestError)) {\n\t\t\t\t\t\terrorsProcessedByHooks.add(error);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error_: unknown) {\n\t\t\tconst normalizedError = normalizeError(error_);\n\t\t\terror = new RequestError(normalizedError.message, normalizedError, this);\n\t\t}\n\n\t\t// Publish error event\n\t\tpublishError({","sourceCodeStart":2494,"sourceCodeEnd":2530,"githubUrl":"https://github.com/sindresorhus/got/blob/e3924aa1e53a6ca3eb93a43618ce532442a89b40/source/core/index.ts#L2494-L2530","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nhooks: {\n  beforeError: [error => { log(error); /* missing return */ }]\n}\n\n// after — always return an Error\nhooks: {\n  beforeError: [error => { log(error); return error; }]\n}\n\n// wrapping\nhooks: {\n  beforeError: [error => new RequestError(`upstream failed: ${error.message}`, error, error.request)]\n}","handlingStrategy":"validation","validationCode":"// Wrap beforeError hooks to guarantee they return an Error.\nfunction wrapBeforeError(hook) {\n  return async (error) => {\n    const next = await hook(error);\n    if (!(next instanceof Error)) {\n      throw new TypeError(`beforeError hook must return an Error instance; received ${typeof next}`);\n    }\n    return next;\n  };\n}\noptions.hooks.beforeError = (options.hooks.beforeError ?? []).map(wrapBeforeError);","typeGuard":"function isErrorInstance(v: unknown): v is Error {\n  return v instanceof Error;\n}","tryCatchPattern":"try {\n  await got(url, { hooks: { beforeError: [hook] } });\n} catch (error) {\n  if (error instanceof TypeError && /beforeError.*hook must return an Error/.test(error.message)) {\n    throw new Error('beforeError hook contract violation — make sure the hook returns an Error', { cause: error });\n  }\n  throw error;\n}","preventionTips":["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."],"tags":["hooks","before-error","error-handling","contract-violation"],"analyzedSha":"e3924aa1e53a6ca3eb93a43618ce532442a89b40","analyzedAt":"2026-08-03T19:22:24.770Z","schemaVersion":2}