denoland/deno · error · Error

ERR_CHILD_PROCESS_IPC_REQUIRED

ERR_CHILD_PROCESS_IPC_REQUIRED

Error message

Forked processes must have an IPC channel, missing value 'ipc' in options.stdio

What it means

fork() starts a child process that talks to the parent over an IPC channel, so options.stdio must include 'ipc'. If stdio is an array without an 'ipc' entry, ERR_CHILD_PROCESS_IPC_REQUIRED is thrown. A stdio string such as 'pipe' is safe: stdioStringToArray() appends 'ipc' automatically.

Solutions

  1. Add 'ipc' to the stdio array: stdio: ['ignore', 'inherit', 'inherit', 'ipc'].
  2. Omit stdio entirely and let fork() configure it, or pass a stdio string.
  3. Use spawn() instead when you do not need channel messaging.

Example fix

// before
fork(workerPath, [], { stdio: ['ignore', 'pipe', 'pipe'] });

// after
fork(workerPath, [], { stdio: ['ignore', 'pipe', 'pipe', 'ipc'] });
Defensive patterns

Strategy: validation

Validate before calling

const stdio = Array.isArray(opts.stdio) ? opts.stdio : null;
if (stdio && !stdio.includes('ipc')) stdio.push('ipc');
fork(modulePath, args, stdio ? { ...opts, stdio } : opts);

Type guard

function hasIpc(stdio: unknown): boolean { return Array.isArray(stdio) && stdio.includes('ipc'); }

Try / catch

try { fork(modulePath, args, opts); } catch (e) { if (e.code === 'ERR_CHILD_PROCESS_IPC_REQUIRED') { opts = { ...opts, stdio: [...(opts.stdio ?? []), 'ipc'] }; fork(modulePath, args, opts); } else throw e; }

Prevention

When it happens

Trigger: fork(modulePath, args, { stdio: ['ignore', 'pipe', 'pipe'] }). Any stdio array like ['ignore'] or ['pipe', 'pipe'] that omits 'ipc'. Copying a spawn() options object into fork() without changes.

Common situations: Shared process options reused for both spawn() and fork(). Code tuned to silence child output ('ignore') that also drops the IPC entry. Migration from spawn() to fork() for messaging without a stdio review.

Related errors


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

Appendix: source

Thrown at ext/node/polyfills/child_process.ts:285

    if (result.traceEventCategories) {
      options.env = {
        ...(options.env ?? lazyProcess().default.env),
        DENO_NODE_TRACE_EVENT_CATEGORIES: result.traceEventCategories,
      };
    }
  }

  if (typeof options.stdio === "string") {
    options.stdio = stdioStringToArray(options.stdio, "ipc");
  } else if (!ArrayIsArray(options.stdio)) {
    // Use a separate fd=3 for the IPC channel. Inherit stdin, stdout,
    // and stderr from the parent if silent isn't set.
    options.stdio = stdioStringToArray(
      options.silent ? "pipe" : "inherit",
      "ipc",
    );
  } else if (!ArrayPrototypeIncludes(options.stdio, "ipc")) {
    throw new ERR_CHILD_PROCESS_IPC_REQUIRED("options.stdio");
  }

  options.execPath = options.execPath || Deno.execPath();
  options.shell = false;

  // deno-lint-ignore no-explicit-any
  (options as any)[kNeedsNpmProcessState] = true;

  return spawn(options.execPath, args, options);
}

function spawn(
  command: string,
  argsOrOptions?: string[] | SpawnOptions,
  maybeOptions?: SpawnOptions,
): ChildProcess {
  const args = ArrayIsArray(argsOrOptions) ? argsOrOptions : [];
  let options = !ArrayIsArray(argsOrOptions) && argsOrOptions != null

View on GitHub (pinned to 9ad36f7a2c)