The child-process module

Reference for node:child_process in Cruft: the synchronous lane (execSync, spawnSync, execFileSync) and the asynchronous lane (spawn, exec, execFile, fork), which options are honored and which are silently ignored. All spawning is capability-gated and denied under sealed mode.

Focused-tier module. Both lanes work: the synchronous functions (execSync, spawnSync, execFileSync) and the asynchronous surface (spawn, exec, execFile, fork). Async spawn delivers stdout data and close events with correct exit codes, and exec invokes its callback with output. The real caveats are at the options level: several common options are silently ignored (see the defects section). All spawning is capability-gated.

Sync-lane options

The synchronous lanes (execSync, spawnSync, execFileSync) honor env and input, and return decoded strings when you pass encoding. spawnSync("printenv", ["V"], { env: { V: "x" } }) sees the supplied variable, and { input: "…" } pipes to the child's stdin. The timeout option is enforced: a child past the budget is terminated and result.error reports spawnSync ETIMEDOUT.

Import forms

const cp = require("node:child_process");  // or "child_process"
import cp from "node:child_process";

Synchronous API

ExportSignatureNotes
execSyncexecSync(command[, options]) -> BufferRuns through the shell; throws Command failed: … (exit N) with stderr on non-zero exit. Returns a Buffer by default (Node-faithful); pass { encoding: "utf8" } to get a decoded string. Honored option: cwd.
spawnSyncspawnSync(command[, args][, options]) -> resultDirect exec, no shell. Never throws: a spawn failure lands in result.error. Result: { status, stdout, stderr, pid, signal, error? }, stdout/stderr are strings, pid is 0, signal is null. Honored options: cwd, timeout (ETIMEDOUT in result.error).
execFileSyncexecFileSync(file[, args][, options])Direct-exec sync variant.

Example:

$ cruft -e 'const cp = require("node:child_process");
console.log(cp.execSync("echo hello").toString().trim());
const r = cp.spawnSync("echo", ["a", "b"]);
console.log(r.status, JSON.stringify(r.stdout));'
hello
0 "a b\n"

Asynchronous API

spawn, exec, execFile, fork, and the promisified exec hook are installed with the Node shapes and the core paths function:

ExportBehavior
spawn(cmd[, args][, opts]) -> ChildProcess.stdout/.stderr emit 'data'; 'close'/'exit' fire with the exit code.
exec(cmd[, opts], cb)Callback invoked with (err, stdout, stderr); non-zero exit produces an error argument.
execFile(file[, args][, opts], cb)Direct-exec async variant.
fork(modulePath[, args][, opts])Present; spawns with 'message' IPC shapes and a _forkChild internal. silent: true does not project child output through child.stdout (output is inherited).

Option handling on the async lanes is not exhaustive; treat option support as conservative (cwd-level).

Capability gating

Spawning is the most dangerous host authority and flows through the capability dispatcher. Default posture runs; --audit logs each spawn (naming the program); --sealed denies:

$ cruft --sealed -e 'require("node:child_process").execSync("echo x")'
cruft: evaluation error: Thrown: TypeError: process.spawn(echo x): no process
capability granted to module '…' (mode: sealed) — hint: add to caps in
package.json: { process: { may_spawn: true } }

Not implemented / gaps

  • execSync returns a Buffer by default (string only with { encoding }); spawnSync results carry string stdout/stderr, pid: 0, signal: null (the two sync lanes decode differently).
  • fork with silent: true does not capture child output.
  • Anything not listed above (detached, stdio arrays, AbortSignal support) is not present.