{"record":{"id":"126897893481a099","repo":"denoland/deno","slug":"err-async-callback","errorCode":"ERR_ASYNC_CALLBACK","errorMessage":"hook.init must be a function","messagePattern":"hook\\.init must be a function","errorType":"error_code","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/internal/async_hooks.ts","lineNumber":429,"sourceCode":"  [after_symbol]: Fn;\n  [destroy_symbol]: Fn;\n  [promise_resolve_symbol]: Fn;\n\n  constructor({\n    init,\n    before,\n    after,\n    destroy,\n    promiseResolve,\n  }: {\n    init: Fn;\n    before: Fn;\n    after: Fn;\n    destroy: Fn;\n    promiseResolve: Fn;\n  }) {\n    if (init !== undefined && typeof init !== \"function\") {\n      throw new ERR_ASYNC_CALLBACK(\"hook.init\");\n    }\n    if (before !== undefined && typeof before !== \"function\") {\n      throw new ERR_ASYNC_CALLBACK(\"hook.before\");\n    }\n    if (after !== undefined && typeof after !== \"function\") {\n      throw new ERR_ASYNC_CALLBACK(\"hook.after\");\n    }\n    if (destroy !== undefined && typeof destroy !== \"function\") {\n      throw new ERR_ASYNC_CALLBACK(\"hook.destroy\");\n    }\n    if (promiseResolve !== undefined && typeof promiseResolve !== \"function\") {\n      throw new ERR_ASYNC_CALLBACK(\"hook.promiseResolve\");\n    }\n\n    this[init_symbol] = init;\n    this[before_symbol] = before;\n    this[after_symbol] = after;\n    this[destroy_symbol] = destroy;","sourceCodeStart":411,"sourceCodeEnd":447,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/internal/async_hooks.ts#L411-L447","documentation":"Thrown by Deno's node:async_hooks polyfill when async_hooks.createHook() receives an `init` field that is neither undefined nor a function. The AsyncHook constructor validates every callback slot (init, before, after, destroy, promiseResolve) before storing it, because the hook machinery later invokes these directly. `null` also triggers it, since the check is `!== undefined`, not falsiness.","triggerScenarios":"Calling async_hooks.createHook({ init: ... }) where init is a string, number, object, null, or the RESULT of a function call instead of the function itself, e.g. createHook({ init: onInit() }) instead of createHook({ init: onInit }).","commonSituations":"Accidentally invoking the callback instead of passing a reference; building the hook config from JSON or a spread object where the function was lost or replaced; defaulting missing callbacks to null instead of omitting the key; DI containers or serializers stripping functions.","solutions":["Pass the function reference without parentheses: { init: onInit }","If the callback is optional, omit the key entirely (or set undefined) — never null","Guard the config before createHook: if (cfg.init !== undefined && typeof cfg.init !== 'function') throw new TypeError(...)","If the config comes from JSON, rebind real functions from a module before creating the hook"],"exampleFix":"// before\nasync_hooks.createHook({ init: handleInit(), before, after, destroy });\n\n// after\nasync_hooks.createHook({ init: handleInit, before, after, destroy });","handlingStrategy":"type-guard","validationCode":"import async_hooks from 'node:async_hooks';\nconst hookConfig = { init, before, after, destroy, promiseResolve };\nfor (const [k, v] of Object.entries(hookConfig)) {\n  if (v !== undefined && typeof v !== 'function') {\n    throw new TypeError(`async_hooks.${k} must be a function, got ${typeof v}`);\n  }\n}\nconst hook = async_hooks.createHook(hookConfig);","typeGuard":"const isCallback = (v) => v === undefined || typeof v === 'function';\nfunction isValidHookConfig(c) {\n  return ['init', 'before', 'after', 'destroy', 'promiseResolve']\n    .every((k) => isCallback(c[k]));\n}","tryCatchPattern":"try {\n  const hook = async_hooks.createHook(cfg);\n} catch (err) {\n  if (err?.code === 'ERR_ASYNC_CALLBACK') {\n    // err.message names the offending field, e.g. 'hook.init must be a function'\n    throw new Error(`Bad async hook config: ${err.message}`);\n  }\n  throw err;\n}","preventionTips":["Always pass function references, never call results","Omit optional callbacks instead of setting null","Never build hook configs from JSON/structuredClone — functions do not survive serialization"],"tags":["async-hooks","node-compat","callback-validation","deno"],"backgroundTag":"invalid-callback-type","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}