nodejs/node · error · GypError
Unknown command string '%s' in '%s'.
Error message
Unknown command string '%s' in '%s'.
What it means
Inside a <! or >! command expansion, GYP recognizes an optional command_string prefix that selects how the command is dispatched. The only supported command_string is 'pymod_do_main'; any other recognized-but-unsupported command_string value raises this GypError listing both the unknown command and the raw contents. This is a parser-level rejection, not a runtime failure of the command itself.
Source
Thrown at tools/gyp/pylib/gyp/input.py:953
sys.path.append(os.getcwd())
try:
parsed_contents = shlex.split(contents)
try:
py_module = __import__(parsed_contents[0])
except ImportError as e:
raise GypError(
"Error importing pymod_do_main"
"module (%s): %s" % (parsed_contents[0], e)
)
replacement = str(
py_module.DoMain(parsed_contents[1:])
).rstrip()
finally:
sys.path.pop()
os.chdir(oldwd)
assert replacement is not None
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"View on GitHub (pinned to 1b2de5e052)
Solutions
- Remove the unsupported prefix and write the command as a plain shell command '<!(my_cmd args)' instead of '<!(prefix args)'.
- If you wanted Python, switch to the supported '<!(pymod_do_main module args)' form and expose DoMain in the module.
- Inspect the regex match in the GypError's 'contents' to see exactly what GYP parsed as command_string vs. contents, then fix the expression accordingly.
Example fix
// before — 'sh' is not a supported command_string 'value': '<!(sh gen_version.sh)', // after — run the shell script directly 'value': '<!(sh gen_version.sh)', # i.e. drop any 'command_string:' prefix the regex mistook; if you had '<!pymod_do_main:...' remove the colon form // correct pymod form: 'value': '<!(pymod_do_main version_helper)',
Defensive patterns
Strategy: validation
Validate before calling
supported = {'pymod_do_main'}
assert command_string in supported or command_string == '', \
f'Unsupported GYP command_string {command_string!r}; use pymod_do_main or a plain shell command' Type guard
def is_supported_command_string(cmd: str) -> bool:
return cmd in ('', 'pymod_do_main') Prevention
- Use only the documented <!() shell form or <!pymod_do_main() — never invent new prefixes.
- When copy-pasting expansions from other build systems, strip their prefix syntax first.
- Lint .gyp files for 'command_string:' patterns that are not 'pymod_do_main:'.
When it happens
Trigger: A .gyp file writes something like '<!(myCmd args)' where 'myCmd' is parsed by GYP's regex as a command_string rather than a plain shell command, or the user assumes GYP supports other prefixed commands. Effectively any command_string that isn't 'pymod_do_main' and isn't empty reaches this branch.
Common situations: Misreading the GYP docs and inventing a command_string prefix; a stray token before the paren that the regex latches onto as a command_string; copy-paste from a non-GYP build system that uses a different prefix syntax.
Related errors
- %s while executing command '%s' in %s
- Call to '%s' returned exit status %d while in %s.
- %s must be a list
- %s %s must be at least length 2, not %s
- %s %s must be followed by a dictionary, not %s
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/644f1d49515b7324.
Report an issue: GitHub.