nodejs/node · error · RuntimeError

Bad command: %s

Error message

Bad command: %s

What it means

tools/install.py run() only handles the commands 'install' and 'uninstall' (each optionally under --headers-only). Any other command string falls through every branch and raises RuntimeError('Bad command: %s').

Source

Thrown at tools/install.py:395

    subdir_files(options, zoslibinc, 'include/node/zoslib/', wanted_zoslib_headers)

def run(options):
  if options.headers_only:
    if options.command == 'install':
      headers(options, install)
      return
    if options.command == 'uninstall':
      headers(options, uninstall)
      return
  else:
    if options.command == 'install':
      files(options, install)
      return
    if options.command == 'uninstall':
      files(options, uninstall)
      return

  raise RuntimeError('Bad command: %s\n' % options.command)

def parse_options(args):
  parser = argparse.ArgumentParser(
      description='Install headers and binaries into filesystem')
  parser.add_argument('command', choices=['install', 'uninstall'])
  parser.add_argument('--dest-dir', help='custom install prefix for packagers, i.e. DESTDIR',
                      default=os.getcwd())
  parser.add_argument('--prefix', help='custom install prefix, i.e. PREFIX',
                      default='/usr/local')
  parser.add_argument('--headers-only', help='only install headers',
                      action='store_true', default=False)
  parser.add_argument('--root-dir', help='the root directory of source code',
                      default=os.getcwd())
  parser.add_argument('--build-dir', help='the location of built binaries',
                      default='out/Release')
  parser.add_argument('--v8-dir', help='the location of V8',
                      default='deps/v8')
  parser.add_argument('--config-gypi-path', help='the location of config.gypi',

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Pass only 'install' or 'uninstall' as the command.
  2. If calling run() directly, validate options.command against {'install','uninstall'} before invoking.
  3. Drive the tool via its CLI (argparse already restricts choices) rather than calling run() by hand.

Example fix

// before
options.command='build'   # run(options) raises 'Bad command'
// after
options.command='install'  # or 'uninstall'
Defensive patterns

Strategy: validation

Validate before calling

assert options.command in ('install','uninstall'), f'Bad command: {options.command}'

Type guard

def is_valid_command(c: str) -> bool:
    return c in ('install','uninstall')

Prevention

When it happens

Trigger: Calling install.py's run(options) programmatically with options.command set to something other than 'install' or 'uninstall', bypassing the argparse `choices` restriction that normally makes this unreachable from the CLI.

Common situations: A wrapper script or build-system integration that constructs options manually and passes a wrong/typo command; legacy callers from before choices were constrained.

Related errors


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/bcef824007b0bbdf. Report an issue: GitHub.