child_process.spawnSync()
Run a command synchronously without a shell and get a result object holding status, stdout and stderr.
child_process.spawnSync(command[, args][, options])- Added in Node.js v0.11.12
Parameters
| Parameter | Type | Description |
|---|---|---|
$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>= command | Explicitly set the value of argv[0] sent to the child process. |
$options.stdiooptional | <string> | <Array>= 'pipe' | Child's stdio configuration. |
$options.envoptional | <Object>= process.env | Environment key-value pairs, replacing the child's environment entirely. |
$options.timeoutoptional | <number>= undefined | Maximum 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 * 1024 | Largest 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>= false | If 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>= false | Hide the subprocess console window that would normally be created on Windows. |
$options.windowsVerbatimArgumentsoptional | <boolean>= false | No 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.errormeans the process never ran (ENOENT, ETIMEDOUT).result.statusis the exit code when it did. Checking only one hides half the failures. encodingdefaults to'buffer', soresult.stdoutis a Buffer unless you pass{ encoding: 'utf8' }.- When the child is killed by a signal,
statusisnullandsignalholds the name. A truthiness check onstatustreats that as success. - With
stdio: 'inherit',stdoutandstderrcome backnull.
Example
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: ENOENTWhen 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.
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.
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: falsePiping 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.
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