{"record":{"id":"df16722dffa1fb04","repo":"denoland/deno","slug":"unhandled-error-in-child-worker","errorCode":null,"errorMessage":"Unhandled error in child worker.","messagePattern":"Unhandled error in child worker\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"runtime/js/11_workers.js","lineNumber":234,"sourceCode":"    while (this.#status === \"RUNNING\") {\n      this.#controlPromise = hostRecvCtrl(this.#id);\n      if (this.#refCount < 1) {\n        core.unrefOpPromise(this.#controlPromise);\n      }\n      const { 0: type, 1: data } = await this.#controlPromise;\n\n      // If terminate was called then we ignore all messages\n      if (this.#status === \"TERMINATED\") {\n        return;\n      }\n\n      switch (type) {\n        case 1: { // TerminalError\n          this.#status = \"CLOSED\";\n        } /* falls through */\n        case 2: { // Error\n          if (!this.#handleError(data)) {\n            throw new Error(\"Unhandled error in child worker.\");\n          }\n          break;\n        }\n        case 3: { // Close\n          log(`Host got \"close\" message from worker: ${this.#name}`);\n          this.#status = \"CLOSED\";\n          return;\n        }\n        default: {\n          throw new Error(`Unknown worker event: \"${type}\"`);\n        }\n      }\n    }\n  };\n\n  #dispatchWorkerMessage(data) {\n    let message, transferables;\n    try {","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/runtime/js/11_workers.js#L216-L252","documentation":"When a worker raises an uncaught error (sync throw, unhandled rejection, or terminal error), the event travels the control channel to the host and Worker dispatches a cancelable ErrorEvent. #handleError in runtime/js/11_workers.js returns event.defaultPrevented; if no listener called preventDefault, the host throws Error('Unhandled error in child worker.'), which surfaces as 'Uncaught (in promise)' and kills the parent by default. Merely logging in an error listener is not enough — the event must be canceled (see tests/specs/worker/worker_error_event, which still crashes despite a logging listener).","triggerScenarios":"`new Worker(url)` with no 'error' listener while the worker throws; an error listener that logs but forgets `event.preventDefault()`; unhandled promise rejections inside the worker; permission-denied errors at worker startup (e.g. a remote specifier without permission), which arrive through the same path.","commonSituations":"Fire-and-forget workers without error wiring; porting browser worker code where unhandled worker errors do not kill the page; nested workers where each level must handle its children or the error cascades.","solutions":["Attach an error handler that cancels the event: `worker.addEventListener('error', (e) => { /* handle */ e.preventDefault(); })`","Fix the underlying worker error — the event carries message, filename, lineno, colno","For startup failures, check permission flags and the Worker `permissions` option covering the worker's specifier","If the parent should die on worker failure, make it explicit: log the event and Deno.exit(1) from your handler"],"exampleFix":"// before\nconst worker = new Worker(url, { type: 'module' });\n// worker throws -> parent crashes: 'Unhandled error in child worker.'\n\n// after\nconst worker = new Worker(url, { type: 'module' });\nworker.addEventListener('error', (e) => {\n  console.error('worker failed:', e.message, `${e.filename}:${e.lineno}`);\n  e.preventDefault(); // mark handled; without this the parent still throws\n});","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"const worker = new Worker(url, { type: 'module' });\nworker.addEventListener('error', (e) => {\n  // handle/log the worker failure...\n  e.preventDefault(); // ...and cancel the event so the host does not rethrow\n});","preventionTips":["Register the error listener immediately after constructing the Worker, before any postMessage","Always call preventDefault() once handled — a listener that only logs still triggers the throw","Apply the same pattern to nested workers: each parent must handle its children or the error cascades","Startup permission failures arrive through this same path — check flags and the Worker permissions option"],"tags":["workers","unhandled-error","error-event","preventdefault","deno"],"backgroundTag":"unhandled-worker-error","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"}