denoland/deno · error · TypeError
ERR_WORKER_UNSUPPORTED_OPERATION
ERR_WORKER_UNSUPPORTED_OPERATION
Error message
Setting trace SIGINT is not supported in workers
What it means
util.setTraceSigInt(enabled) toggles Node's process-wide SIGINT tracing hook, which is main-thread-only. Deno's polyfill accepts the call on the main thread (as a documented no-op, since Deno has no SIGINT trace facility) but throws ERR_WORKER_UNSUPPORTED_OPERATION inside worker threads, mirroring Node: a worker cannot install the process signal tracer.
Source
Thrown at ext/node/polyfills/util.ts:377
return capturedTraces;
}
function parseEnv(input) {
validateString(input, "content");
const parsed = binding.parseEnv(input);
const result = ObjectCreate(null);
const keys = ObjectKeys(parsed);
for (let i = 0; i < keys.length; i++) {
result[keys[i]] = parsed[keys[i]];
}
return result;
}
function setTraceSigInt(enabled) {
validateBoolean(enabled, "enabled");
if (internals.__isWorkerThread) {
throw new ERR_WORKER_UNSUPPORTED_OPERATION("Setting trace SIGINT");
}
// No-op on the main thread: Deno does not implement Node's SIGINT trace
// facility, but the call should succeed so user code can opt in/out.
}
// https://nodejs.org/api/util.html#utilqueryobjectsconstructor-options
// Mirrors `v8.queryObjects` - see ext/node/polyfills/v8.ts for the limitations.
function queryObjects(ctor, options) {
return lazyV8().queryObjects(ctor, options);
}
// Deno's AbortSignal is not yet structured-cloneable, so transfer is a no-op:
// the returned signal/controller can still be used in-process. This mirrors
// Node's API surface so user code calling these does not throw.
function transferableAbortSignal(signal) {
if (
signal === null || typeof signal !== "object" ||
typeof signal.aborted !== "boolean"View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Guard the call: only invoke setTraceSigInt when worker_threads.isMainThread is true.
- Move setTraceSigInt out of module top-level into an explicit main-thread init function.
- If tracing is required, restructure so that code path only runs in the main process, not in pool workers.
Example fix
// before — module top level, runs in workers too
util.setTraceSigInt(true); // throws ERR_WORKER_UNSUPPORTED_OPERATION in a worker
// after
import { isMainThread } from "node:worker_threads";
if (isMainThread) util.setTraceSigInt(true); Defensive patterns
Strategy: validation
Validate before calling
import { isMainThread } from "node:worker_threads";
import util from "node:util";
export function safeSetTraceSigInt(enabled) {
if (!isMainThread) return; // no-op in workers instead of throwing
util.setTraceSigInt(enabled);
} Prevention
- Never put process-wide signal configuration at module top level; workers import the same module.
- Audit process-global APIs (setTraceSigInt, exit hooks) for code shared with workers.
- Remember Deno implements this as a no-op even on the main thread — do not rely on observable tracing effects.
When it happens
Trigger: Calling util.setTraceSigInt(true) from code running inside a Worker — new Worker(...), worker_threads, or pooled runtimes (Piscina, workerpool, test workers).
Common situations: CLI libraries that enable SIGINT tracing at import time and then get loaded inside Jest workers or task pools; module top-level side effects that never check isMainThread; code shared between a main process and its workers.
Related errors
- ERR_INSPECTOR_NOT_WORKER
- Unable to deserialize RecordableHistogram
- ERR_WORKER_UNSUPPORTED_OPERATION
- ERR_WORKER_INVALID_EXEC_ARGV
- ERR_INVALID_URL_SCHEME
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/d5891ca4265c00aa.
Report an issue: GitHub.