{"record":{"id":"bc4e5ce9095d6c11","repo":"denoland/deno","slug":"err-unavailable-during-exit","errorCode":"ERR_UNAVAILABLE_DURING_EXIT","errorMessage":"Cannot call function in process exit handler","messagePattern":"Cannot call function in process exit handler","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/internal/assert/calltracker.js","lineNumber":112,"sourceCode":"  }\n\n  reset(tracked) {\n    if (tracked === undefined) {\n      SetPrototypeForEach(this.#callChecks, (check) => check.reset());\n      return;\n    }\n\n    this.#getTrackedFunction(tracked).reset();\n  }\n\n  getCalls(tracked) {\n    return this.#getTrackedFunction(tracked).getCalls();\n  }\n\n  calls(fn, expected = 1) {\n    // deno-lint-ignore no-process-global\n    if (process._exiting) {\n      throw new ERR_UNAVAILABLE_DURING_EXIT();\n    }\n    if (typeof fn === \"number\") {\n      expected = fn;\n      fn = noop;\n    } else if (fn === undefined) {\n      fn = noop;\n    }\n\n    validateUint32(expected, \"expected\", true);\n\n    const context = new CallTrackerContext({\n      expected,\n      // eslint-disable-next-line no-restricted-syntax\n      stackTrace: new Error(),\n      name: fn.name || \"calls\",\n    });\n    const tracked = new Proxy(fn, {\n      __proto__: null,","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/internal/assert/calltracker.js#L94-L130","documentation":"CallTracker.calls() refuses to register new tracked functions once the process has begun exiting (process._exiting is set when the 'exit' event is imminent). Any tracker.calls(...) executed in that window throws ERR_UNAVAILABLE_DURING_EXIT, because assertions registered after exit could never be verified.","triggerScenarios":"tracker.calls(fn) inside a process.on('exit', ...) handler; registering mocks from teardown code that runs during shutdown; late-arriving async work that wraps functions after exit has started.","commonSituations":"Test harnesses installing exit-time cleanup that also sets up mocks; signal handlers (SIGTERM/SIGINT) that lazily instrument code during forced shutdown.","solutions":["Move tracker.calls() registration into normal setup (module load / test start), never into exit or teardown hooks.","In exit handlers, only call tracker.verify()/report() on functions registered earlier — those remain allowed.","Restructure shutdown code so no new tracking is created after exit begins."],"exampleFix":"// before\nprocess.on('exit', () => {\n  const t = tracker.calls(cleanup); // ERR_UNAVAILABLE_DURING_EXIT\n  cleanup();\n  tracker.verify();\n});\n\n// after\nconst t = tracker.calls(cleanup);\nprocess.on('exit', () => {\n  cleanup();\n  tracker.verify();\n});","handlingStrategy":"validation","validationCode":"function safeCalls(tracker, fn, expected = 1) {\n  if (process._exiting) {\n    throw new Error('cannot register tracked functions during process exit');\n  }\n  return tracker.calls(fn, expected);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Register all tracked functions during setup, before any exit handler can run.","Never create mocks inside 'exit'/'beforeExit' listeners or signal-driven shutdown paths.","Limit exit handlers to verify()/report() on already-registered functions."],"tags":["testing","assert","process-exit","calltracker","node-compat"],"backgroundTag":"api-unavailable-during-exit","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}