child_process.spawnSync()

Run a command synchronously without a shell and get a result object holding status, stdout and stderr.

Signature
child_process.spawnSync(command[, args][, options])
  • Added in Node.js v0.11.12

Parameters

ParameterTypeDescription
$command<string>The command to run. Without a shell this is an executable name or path, not a command line.
$argsoptional<string[]>= []List of string arguments, each passed to the program verbatim.
$optionsoptional<Object>Spawn options.
$options.cwdoptional<string> | <URL>= process.cwd()Current working directory of the child process.
$options.inputoptional<string> | <Buffer> | <TypedArray> | <DataView>Value passed as stdin to the spawned process. Supplying it overrides stdio[0].
$options.argv0optional<string>= commandExplicitly set the value of argv[0] sent to the child process.
$options.stdiooptional<string> | <Array>= 'pipe'Child's stdio configuration.
$options.envoptional<Object>= process.envEnvironment key-value pairs, replacing the child's environment entirely.
$options.timeoutoptional<number>= undefinedMaximum milliseconds the process is allowed to run before killSignal is sent.
$options.killSignaloptional<string> | <integer>= 'SIGTERM'Signal used when the spawned process is killed.
$options.maxBufferoptional<number>= 1024 * 1024Largest amount of data in bytes allowed on stdout or stderr.
$options.encodingoptional<string>= 'buffer'Encoding used for all stdio inputs and outputs. Defaults to 'buffer', so stdout is a Buffer unless set.
$options.shelloptional<boolean> | <string>= falseIf true, runs command inside a shell ('/bin/sh' on Unix, process.env.ComSpec on Windows).
$options.uidoptional<number>Sets the user identity of the process (see setuid(2)).
$options.gidoptional<number>Sets the group identity of the process (see setgid(2)).
$options.windowsHideoptional<boolean>= falseHide the subprocess console window that would normally be created on Windows.
$options.windowsVerbatimArgumentsoptional<boolean>= falseNo quoting or escaping of arguments is done on Windows. Ignored on Unix.

Return value

<Object>An object with pid, output (an array of stdio results), stdout, stderr, status (exit code or null), signal (name or null) and error (set only when the process failed to start or timed out).

Errors

Does not throw for a failing or missing command. Failure to spawn, a timeout, or exceeding maxBuffer is reported in the returned object's error property, while a non-zero exit shows up as status.

Changelog

  • PHP v16.4.0, v14.18.0The cwd option can be a WHATWG URL object using the file: protocol.
  • PHP v10.10.0The input option can be any TypedArray or a DataView.
  • PHP v8.8.0The windowsHide option is supported.
  • PHP v8.0.0The input option can be a Uint8Array.
  • PHP v6.2.1, v4.5.0The encoding option can now explicitly be set to 'buffer'.
  • PHP v5.7.0The shell option is supported.

spawnSync() blocks the whole process until the command exits, then hands back a plain object describing what happened. It is the synchronous sibling of spawn() and, like it, uses no shell by default.

When to reach for it

Use it in scripts and CLIs where you want to inspect an exit code without exception handling. Because it never throws for a failed command, it suits probing — "is docker installed?", "did the linter find anything?" — far better than execSync(), which turns every non-zero status into a throw. It is also the safe synchronous option, since arguments go in an array rather than a shell string.

What trips people up

  • There are two failure channels. result.error means the process never ran (ENOENT, ETIMEDOUT). result.status is the exit code when it did. Checking only one hides half the failures.
  • encoding defaults to 'buffer', so result.stdout is a Buffer unless you pass { encoding: 'utf8' }.
  • When the child is killed by a signal, status is null and signal holds the name. A truthiness check on status treats that as success.
  • With stdio: 'inherit', stdout and stderr come back null.

Example

Example · javascript
const { spawnSync } = require('node:child_process');

const ok = spawnSync('node', ['-p', '40 + 2'], { encoding: 'utf8' });
console.log('status:', ok.status);
console.log('stdout:', JSON.stringify(ok.stdout));
console.log('error:', ok.error);

// A missing binary is reported in `error`, not thrown, and status stays null.
const missing = spawnSync('definitely-not-a-command', [], { encoding: 'utf8' });
console.log('missing status:', missing.status);
console.log('missing error code:', missing.error && missing.error.code);

// status: 0
// stdout: "42\n"
// error: undefined
// missing status: null
// missing error code: ENOENT

When to use it

  • Check whether a required CLI tool is installed before a script continues.
  • Run a linter or test command and branch on its exit code without try/catch.
  • Pipe a string through a system filter such as sort or gzip in a build step.

Frequently asked questions

What is the difference between spawnSync and execSync?

spawnSync() returns a result object and never throws for a failing command, while execSync() runs the command through a shell and throws on any non-zero exit. spawnSync() also takes arguments as an array, so it is safe to build from variables.

Why is spawnSync stdout null?

stdout is null when that stream was not piped back, which happens with stdio: 'inherit' or 'ignore', and also when the process never started, in which case result.error is set. With the default stdio: 'pipe' you get a Buffer unless you pass encoding: 'utf8'.

How do I get the exit code of a command in Node.js synchronously?

Read the status property of the object returned by spawnSync(). It holds the exit code, or null when the process was killed by a signal, in which case the signal name is in the signal property.

More examples

Checking every failure channel

The four calls exercise every outcome spawnSync() can produce. Note that a timeout surfaces through error (as ETIMEDOUT), not through status, which is why the checks must run in this order. Testing signal separately matters because a killed process has status null, which is not equal to 0 but is also not a real exit code.

Example · javascript
const { spawnSync } = require('node:child_process');

function run(cmd, args, options = {}) {
  const r = spawnSync(cmd, args, { encoding: 'utf8', ...options });
  if (r.error) return { ok: false, reason: 'could not run: ' + r.error.code };
  if (r.signal) return { ok: false, reason: 'killed by ' + r.signal };
  if (r.status !== 0) return { ok: false, reason: 'exited ' + r.status };
  return { ok: true, out: r.stdout.trim() };
}

console.log(run('node', ['-p', '"fine"']));
console.log(run('node', ['-e', 'process.exit(7)']));
console.log(run('nope', []));
console.log(run('node', ['-e', 'setInterval(() => {}, 1000)'], { timeout: 300 }));

// { ok: true, out: 'fine' }
// { ok: false, reason: 'exited 7' }
// { ok: false, reason: 'could not run: ENOENT' }
// { ok: false, reason: 'could not run: ETIMEDOUT' }

Probing for an installed tool

This is the pattern execSync() cannot express cleanly, because there the missing binary and the non-zero exit would both throw and have to be untangled in a catch block. With spawnSync() the answer is a boolean. stdio: 'ignore' keeps the version banner off the terminal and avoids buffering output nobody reads.

Example · javascript
const { spawnSync } = require('node:child_process');

function isInstalled(binary) {
  // stdio 'ignore' discards output; we only care whether it ran and succeeded.
  const r = spawnSync(binary, ['--version'], { stdio: 'ignore' });
  return !r.error && r.status === 0;
}

console.log('node present:', isInstalled('node'));
console.log('fake tool present:', isInstalled('totally-not-installed'));

// node present: true
// fake tool present: false

Piping data through a filter

input writes the string to the child's stdin and closes it, so no temporary file is needed. The output array is the raw form of the result, with one entry per stdio stream: output[0] is always null for stdin, output[1] is stdout and output[2] is stderr, which is why its length is 3.

Example · javascript
const { spawnSync } = require('node:child_process');

const r = spawnSync('sort', ['-n'], {
  input: '30\n4\n100\n',
  encoding: 'utf8',
});

console.log('status:', r.status);
console.log('sorted:', JSON.stringify(r.stdout));
console.log('output array length:', r.output.length);

// status: 0
// sorted: "4\n30\n100\n"
// output array length: 3

Discussion

  • Be the first to comment on this lesson.