denoland/deno · error · Error
Unhandled error in child worker.
Error message
Unhandled error in child worker.
What it means
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).
Source
Thrown at runtime/js/11_workers.js:234
while (this.#status === "RUNNING") {
this.#controlPromise = hostRecvCtrl(this.#id);
if (this.#refCount < 1) {
core.unrefOpPromise(this.#controlPromise);
}
const { 0: type, 1: data } = await this.#controlPromise;
// If terminate was called then we ignore all messages
if (this.#status === "TERMINATED") {
return;
}
switch (type) {
case 1: { // TerminalError
this.#status = "CLOSED";
} /* falls through */
case 2: { // Error
if (!this.#handleError(data)) {
throw new Error("Unhandled error in child worker.");
}
break;
}
case 3: { // Close
log(`Host got "close" message from worker: ${this.#name}`);
this.#status = "CLOSED";
return;
}
default: {
throw new Error(`Unknown worker event: "${type}"`);
}
}
}
};
#dispatchWorkerMessage(data) {
let message, transferables;
try {View on GitHub (pinned to 9ad36f7a2c)
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
Example fix
// before
const worker = new Worker(url, { type: 'module' });
// worker throws -> parent crashes: 'Unhandled error in child worker.'
// after
const worker = new Worker(url, { type: 'module' });
worker.addEventListener('error', (e) => {
console.error('worker failed:', e.message, `${e.filename}:${e.lineno}`);
e.preventDefault(); // mark handled; without this the parent still throws
}); Defensive patterns
Strategy: try-catch
Try / catch
const worker = new Worker(url, { type: 'module' });
worker.addEventListener('error', (e) => {
// handle/log the worker failure...
e.preventDefault(); // ...and cancel the event so the host does not rethrow
}); Prevention
- 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
When it happens
Trigger: `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.
Common situations: 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.
Related errors
- Can't convert url ("{}") to filename.
- Can't convert url ("{}") to filename.
- ${prefix}Linter plugin name must only contain lowercase lett
- ${prefix}Linter plugin name must start and end with a lowerc
- ${prefix}Linter plugin name must not have consequtive hyphen
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/df16722dffa1fb04.
Report an issue: GitHub.