nodejs/node · error · GypError
%s while executing command '%s' in %s
Error message
%s while executing command '%s' in %s
What it means
When a <!() (or >!) shell-command expansion is evaluated, GYP runs the command via subprocess.run with shell=use_shell and cwd=build_file_dir. If subprocess.run itself raises (e.g. FileNotFoundError because the executable is missing, or a permission error spawning the process), GYP wraps that exception in this GypError, embedding the exception, the command string, and the build file. This is distinct from a non-zero exit code (see error 484).
Source
Thrown at tools/gyp/pylib/gyp/input.py:970
elif command_string:
raise GypError(
"Unknown command string '%s' in '%s'."
% (command_string, contents)
)
else:
# Fix up command with platform specific workarounds.
contents = FixupPlatformCommand(contents)
try:
# stderr will be printed no matter what
result = subprocess.run(
contents,
stdout=subprocess.PIPE,
shell=use_shell,
cwd=build_file_dir,
check=False,
)
except Exception as e:
raise GypError(
"%s while executing command '%s' in %s"
% (e, contents, build_file)
)
if result.returncode > 0:
raise GypError(
"Call to '%s' returned exit status %d while in %s."
% (contents, result.returncode, build_file)
)
replacement = result.stdout.decode("utf-8").rstrip()
cached_command_results[cache_key] = replacement
else:
gyp.DebugOutput(
gyp.DEBUG_VARIABLES,
"Had cache value for command '%s' in directory '%s'",
contents,
build_file_dir,View on GitHub (pinned to 1b2de5e052)
Solutions
- Read the embedded exception text — FileNotFoundError means the executable is not on PATH, so install it or fix the path.
- If using the array form '<!@[cmd, args]', make sure every element is a real argv entry and the command resolves absolutely.
- Verify build_file_dir (the .gyp file's directory) exists and is accessible from gyp's cwd.
- Run the exact command string manually from the .gyp file's directory to reproduce the spawn failure.
Example fix
// before — 'my_codegen' not on PATH 'sources': ['<!(my_codegen --list)'], // after — point at the script's real location or install it 'sources': ['<!(./tools/my_codegen --list)'],
Defensive patterns
Strategy: validation
Validate before calling
import shutil, os
for tool in required_tools:
assert shutil.which(tool) or os.path.exists(tool), f'Tool for <() expansion not found: {tool}' Type guard
def is_command_runnable(cmd: str, cwd: str) -> bool:
import shutil, os
first = shutil._which if False else __import__('shlex').split(cmd)[0]
return bool(__import__('shutil').which(first)) and os.path.isdir(cwd or '.') Try / catch
try:
gyp.process_build_file(...)
except gyp.input.GypError as e:
if 'while executing command' in str(e):
diagnose_missing_tool(str(e)) Prevention
- Run each <!() command manually from the .gyp file's directory before committing.
- Pin build-time tool versions in the same environment gyp runs in.
- Avoid array form '<!@[...]' unless every argv element is real and the executable resolves.
When it happens
Trigger: The command referenced by '<!(foo args)' names an executable not present on PATH; the command string is malformed so the shell cannot spawn; the working directory build_file_dir does not exist or is not readable; the OS refuses to spawn (permissions, OOM, too many fds).
Common situations: Forgetting to install a build-time code generator (protoc, gperf, a vendored script); running gyp in an environment without a required tool on PATH; a path with spaces or shell metacharacters breaking the spawn when use_shell is False (array form); a stale build_file_dir after a tree reorganization.
Related errors
- Call to '%s' returned exit status %d while in %s.
- Unknown command string '%s' in '%s'.
- Error %d running %s
- %s is missing - make sure VC++ tools are installed.
- %s requires any SDK of %s version, but none were found
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/a01481a60caf4074.
Report an issue: GitHub.