Repository navigation
child_process.spawn fails on Windows given a space in both the command and an argument #7367
Description
Activity
- addedchild_processIssues and PRs related to the child_process subsystem.Issues and PRs related to the child_process subsystem.windowsIssues and PRs related to the Windows platform.Issues and PRs related to the Windows platform.
on Jun 22, 2016 /cc @nodejs/platform-windows
I can reproduce, working on a fix.
I've had success using nodejs/node-v0.x-archive#25895 (comment) from the linked issue as a work around.
Is this an issue with
exec()too?They are related,
execinternally callsspawn.The problem is in how shell commands are executed under Windows. I have a fix almost ready, I'll publish it probably tomorrow.
Reacted by Matteo CollinaDoes this not happen on v4?
exec internally calls spawn.
I get that. I was asking if
exec()was affected as well, since it does some manipulation before passing to spawn.@mcollina Problem exists on v4.4.4 as well.
@s100 can you check on v4.4.7
LTS is affected for sure thats what I'm using.
Im my case we are only using spawn but given its at the point of exec I assume its affected too.
@thealphanerd still present on v4.4.7. Any others?
Unfortunately I haven't been able to find a fix that would not also break a lot of other use cases. But there is luckily easy work around for this issue - you need to quote script filename and add
shell:trueto options:spawn('"with spaces.cmd"', ['arg with spaces'], { shell: true });Reacted by Andrew Costello, Logan, dreamlin517, Aditya Karnam, Adrian Moisa, wangchaotv, Ibrahim AHMED BACHA, Justin, Daniel Jenta, Sergei Startsev and 3 moreReacted by Logan and Aditya Karnam4 remaining items
@s100 sorry, I didn't make myself clear and provide summary. My bad.
The thing is, there is no way to fix this. When calling shell scripts under Windows, you need to add
{shell: true}to make sure things work ok (doc link). Shell scripts are not executable files under Windows, so they need special treatment. Also, Windows takes all arguments as single strings, not as array like Linux does.When the script filename has space in it, it needs to be quoted. We cannot do this automatically -
spawnis used byexec, for which users pass command and arguments as a single string. If we would add quotes we would break usages like in test-exec.js:93.FWIW I've opened a PR to add a example to the documentation: #8035
Reacted by NateReacted by Wédney Yuri- added a commit that references this issue
on Aug 18, 2016 - added a commit that references this issue
on Aug 24, 2016 When the script filename has space in it, it needs to be quoted. We cannot do this automatically -
spawnis used byexec, for which users pass command and arguments as a single string. If we would add quotes we would break usages like in test-exec.js:93.How does that follow?
execon Windows is implemented in terms ofspawn(…, { windowsVerbatimArguments: true }), but shouldn’tspawnwithoutwindowsVerbatimArgumentsquote and escape the command and arguments?Currently there’s no way to make a command like
childProcess.spawn('npm', ['install', 'eslint@>=4.1.1'])work in a cross-platform way. It works on Unix, but as written it fails with ENOENT on Windows, and with{ shell: true }the shell mangles the command and redirects the output ofnpm install eslint@to a file named=4.1.1. The latter failure also happens without{ shell: true }if the'npm'is changed to'npm.cmd'.For anyone who comes here just looking for a workaround like I did, the cross-spawn package seems to implement the right API.
Reacted by Chris Cowan// DON'T WRITE THE
http://
var options = {
host: 'http://yoururl.com',
path: '/path/to/resource'
};I just spent hours trying to figure out why electron-reloader failed to restart electron for me, and it ultimately had to do with the directory containing spaces, it trying to execute the ".cmd" executable created by npm for electron while passing it the directory (containing spaces) as an argument, and this issue popping up.
Rust's standard library has an API very similar to
child_process.spawnwhich correctly handles this case of running .bat/.cmd files containing spaces being passed arguments with spaces, so the problem seems possible to solve:use std::process::Command; fn main() { let ecode = Command::new("C:\\path with spaces\\a b.cmd") .args(["xx yy", "1 2"]) .spawn() .expect("failed to execute process") .wait() .expect("failed to wait on child"); assert!(ecode.success()); }
It turns out Rust had this same problem before and solved it in rust-lang/rust#95246, by specifically handling the case where the program's filename ends with ".bat" or ".cmd" and creating a full command string involving "cmd /C ..." with the program and the arguments each escaped as necessary.
It's true that child_process's docs tell users to turn on
shell: trueand escape the command and arguments themselves when a .bat/.cmd is being executed, but this isn't enough to prevent buggy software:- Unlike what the docs imply,
child_process.spawn/execFile/etc right now mostly work on .bat/.cmd files without doing shell:true and manual escaping. It only seems to break in this case involving spaces both in the program and arguments. Plenty of people will write their code in the naive way, find it works, and not think about it twice. Even if they notice the warning in the docs, given that their code works they may assume the docs are outdated or not actually relevant to them. (And even if they do notice the doc and decide to change their code to follow its recommendation, it will result in their code getting uglier.) - npm on Windows creates .cmd files for the executables in dependencies, so .cmd files are commonly encountered and executed in the Node ecosystem.
- npm on non-Windows platforms creates "real" executables for the executables in dependencies, so someone coding on a non-Windows platform would not expect that they have to do anything special in order to execute executables from dependencies, and their code will be more fragile on Windows than they would expect.
If Node copied Rust's behavior, it would make a ton of existing programs more dependable and stop the years of people still running into this issue.
This would not conflict with #29532 / #29576, because those are only about making arguments work more consistently in the
shell: truecase as with theshell: falsecase, which seems sensible regardless of it being motivated by making it easier to intentionally work around this issue. Adopting Rust's behavior would only change the handling of the program argument in theshell: false(and program ends-with .bat/.cmd) case, and would make it unnecessary for people to have to identify and work around this issue.Reacted by Grigory and surendrajat- Unlike what the docs imply,
- added a commit that references this issue
on Jul 23, 2023 - added a commit that references this issue
on Nov 5, 2024 - added 2 commits that reference this issue
on Nov 17, 2025 - added 2 commits that reference this issue
on Aug 27, 2026
child_processReproduction of the error: https://gist.github.com/smrq/f028b22bc748af9e68a7
On Windows,
child_process.spawnhandles the command incorrectly when both it and one of its arguments contains a space. So, this works fine:But this yields
'command' is not recognized as an internal or external command, operable program or batch file.:(This is node-v0.x-archive #25895, still extant in higher Node.js versions.)