sindresorhus/got · error · TypeError
beforeCache hook must return false or undefined. To modify t
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.
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)
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 P
- The `afterResponse` hook returned an invalid value
- The `beforeError` hook must return an Error instance. Receiv
- ERR_RETRYING
- A retry listener has been attached already.
AI-assisted analysis of sindresorhus/got@e3924aa1e5 (2026-08-03).
Data as JSON: /data/errors/dde645746c7f40eb.json.
Report an issue: GitHub.