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

  1. Guard the call: only invoke setTraceSigInt when worker_threads.isMainThread is true.
  2. Move setTraceSigInt out of module top-level into an explicit main-thread init function.
  3. 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

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


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/d5891ca4265c00aa. Report an issue: GitHub.