sindresorhus/got · error · TypeError
beforeCache hook must return false or undefined. To modify…
Error message
beforeCache hook must return false or undefined. To modify the response, mutate it directly.
What it means
Thrown at source/core/index.ts:2031 in the same beforeCache driver. Even if the hook is synchronous, it must still return one of two values: `false` (meaning 'do not cache this response', which the driver honors by adding no-cache headers and short-circuiting remaining hooks) or `undefined`/void (meaning 'continue, mutations applied directly'). Any other return value — a response object, a truthy boolean, a string — is rejected because the hook contract is mutate-in-place, not return-new-value.
Solutions
- Mutate the response argument in place (e.g. `response.headers['x'] = 'y'`) and return nothing — implicit undefined is correct.
- Return literally `false` if you want to skip caching this response.
- Do not return the response, an object, or any truthy non-false value.
Example fix
// before — wrong contract, returns the response
hooks: { beforeCache: [response => { response.headers.x = '1'; return response; }] }
// after — mutate in place, return nothing
hooks: { beforeCache: [response => { response.headers.x = '1'; }] }
// or signal 'do not cache'
hooks: { beforeCache: [response => response.statusCode === 500 ? false : undefined] } Defensive patterns
Strategy: validation
Validate before calling
// Wrap beforeCache hooks to enforce the return contract.
function wrapBeforeCache(hook) {
return (response) => {
const result = hook(response);
if (result !== undefined && result !== false) {
throw new TypeError('beforeCache hook must return false or undefined — mutate the response directly instead.');
}
return result;
};
}
options.hooks.beforeCache = (options.hooks.beforeCache ?? []).map(wrapBeforeCache); Type guard
type BeforeCacheResult = false | undefined | void;
function isValidBeforeCacheReturn(v: unknown): v is BeforeCacheResult {
return v === undefined || v === false;
} Try / catch
try {
await got(url, options);
} catch (error) {
if (error instanceof TypeError && /beforeCache hook must return false or undefined/.test(error.message)) {
throw new Error('beforeCache hook returned an invalid value — mutate response in place, return nothing.', { cause: error });
}
throw error;
} Prevention
- Mutate the response argument in place; do not return it.
- Return literally `false` to skip caching; otherwise return nothing.
- Do not copy the afterResponse idiom (which returns the response) into beforeCache.
When it happens
Trigger: Returning the response object from a beforeCache hook (mirroring afterResponse's pattern, which is wrong here); returning `true` to mean 'yes cache it'; returning a status code or header value.
Common situations: Developers familiar with afterResponse (where you return the response) applying that idiom to beforeCache; refactoring a caching integration; copy-pasting hook shapes across hook types.
Related errors
- beforeCache hooks must be synchronous. The hook returned a…
- The `afterResponse` hook returned an invalid value
- The `beforeError` hook must return an Error instance…
- A retry listener has been attached already.
- ERR_RETRYING
AI-assisted analysis of sindresorhus/got@e3924aa1e5 (2026-08-03).
Data as JSON: /api/errors/dde645746c7f40eb.
Report an issue: GitHub.
Appendix: source
Thrown at source/core/index.ts:2031
// Prevent caching by adding no-cache headers
// Mutate the response directly to add headers
response.headers['cache-control'] = 'no-cache, no-store, must-revalidate';
response.headers.pragma = 'no-cache';
response.headers.expires = '0';
handler(response);
// Don't call remaining hooks - we've decided not to cache
return;
}
if (is.promise(result)) {
// BeforeCache hooks must be synchronous because cacheable-request's handler is synchronous
throw new TypeError('beforeCache hooks must be synchronous. The hook returned a Promise, but this hook must return synchronously. If you need async logic, use beforeRequest hook instead.');
}
if (result !== undefined) {
// Hooks should return false or undefined only
// Mutations work directly - no need to return the response
throw new TypeError('beforeCache hook must return false or undefined. To modify the response, mutate it directly.');
}
// Else: void/undefined = continue
}
} catch (error: unknown) {
const normalizedError = normalizeError(error);
// Convert hook errors to RequestError and propagate
// This is consistent with how other hooks handle errors
if (gotRequest) {
gotRequest._beforeError(normalizedError instanceof RequestError ? normalizedError : new RequestError(normalizedError.message, normalizedError, gotRequest));
// Don't call handler when error was propagated successfully
return;
}
// If gotRequest is missing, log the error to aid debugging
// We still call the handler to prevent the request from hanging
console.error('Got: beforeCache hook error (request context unavailable):', normalizedError);
// Call handler with response (potentially partially modified)
handler(response);View on GitHub (pinned to e3924aa1e5)