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

  1. Mutate the response argument in place (e.g. `response.headers['x'] = 'y'`) and return nothing — implicit undefined is correct.
  2. Return literally `false` if you want to skip caching this response.
  3. 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

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


AI-assisted analysis of sindresorhus/got@e3924aa1e5 (2026-08-03). Data as JSON: /data/errors/dde645746c7f40eb.json. Report an issue: GitHub.