{"record":{"id":"4fe4b00e4f98efcf","repo":"nodejs/node","slug":"callback-must-be-a-function","errorCode":null,"errorMessage":"callback must be a function","messagePattern":"callback must be a function","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"deps/undici/src/lib/handler/cache-revalidation-handler.js","lineNumber":45,"sourceCode":"   * @type {(import('../../types/dispatcher.d.ts').default.DispatchHandler)}\n   */\n  #handler\n\n  #context\n\n  /**\n   * @type {boolean}\n   */\n  #allowErrorStatusCodes\n\n  /**\n   * @param {(success: boolean, context?: any, statusCode?: number, headers?: import('../../types/header.d.ts').IncomingHttpHeaders) => void} callback Function to call if the cached value is valid\n   * @param {import('../../types/dispatcher.d.ts').default.DispatchHandlers} handler\n   * @param {boolean} allowErrorStatusCodes\n   */\n  constructor (callback, handler, allowErrorStatusCodes) {\n    if (typeof callback !== 'function') {\n      throw new TypeError('callback must be a function')\n    }\n\n    this.#callback = callback\n    this.#handler = handler\n    this.#allowErrorStatusCodes = allowErrorStatusCodes\n  }\n\n  onRequestStart (_, context) {\n    this.#successful = false\n    this.#context = context\n  }\n\n  onRequestUpgrade (controller, statusCode, headers, socket) {\n    this.#handler.onRequestUpgrade?.(controller, statusCode, headers, socket)\n  }\n\n  onResponseStart (\n    controller,","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/deps/undici/src/lib/handler/cache-revalidation-handler.js#L27-L63","documentation":"Thrown by the CacheRevalidationHandler constructor when callback is not a function. The handler invokes callback(success, context, statusCode, headers) to report whether a cached entry is still valid after a revalidation round-trip (e.g. HTTP 304); a non-function callback would silently drop that signal. Note this throws TypeError, not InvalidArgumentError.","triggerScenarios":"Constructing new CacheRevalidationHandler(null, handler), new CacheRevalidationHandler({}, handler), or omitting the first argument. The first parameter must be the revalidation-result callback.","commonSituations":"Passing the wrapped handler as the first arg by mistake; passing a config object where the callback was expected; refactoring that swapped argument order; building a custom cache layer that forgets the callback.","solutions":["Pass a function as the first argument: (success, context, statusCode, headers) => { ... }.","Double-check argument order: callback first, then the downstream DispatchHandler.","If you do not need revalidation feedback, pass a no-op () => {} rather than omitting it.","Type-check the callback in your cache wrapper before constructing the handler."],"exampleFix":"// before\nnew CacheRevalidationHandler(downstreamHandler)\n// after\nnew CacheRevalidationHandler((ok, ctx, code, hdrs) => { if (!ok) evict(ctx) }, downstreamHandler)","handlingStrategy":"type-guard","validationCode":"if (typeof callback !== 'function') {\n  throw new TypeError('CacheRevalidationHandler requires a function callback as its first argument')\n}\nnew CacheRevalidationHandler(callback, handler)","typeGuard":"function isCallback(v) { return typeof v === 'function' }","tryCatchPattern":null,"preventionTips":["Remember argument order: callback first, downstream handler second.","Pass a no-op () => {} when you do not need revalidation feedback.","Type-check callback in your cache wrapper before constructing the handler."],"tags":["undici","cache","handler","invalid-arg","typeerror"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}