nodejs/node · error · Exception

: exit status

Error message

%s: exit status %d

What it means

Raised by RunCmd in tools/inspector_protocol/roll.py when a subprocess launched via subprocess.Popen returns a non-zero exit code. roll.py is the script that rolls (syncs) a vendored copy of the inspector_protocol dependency into a host repo (e.g. Chromium/devtools-protocol); RunCmd wraps every git/external command and treats any failure as fatal. NOTE: the raise is itself buggy - it uses 'raise Exception(fmt, args...) with a comma instead of 'raise Exception(fmt % args)', so the formatted message is never produced; the exception carries the raw tuple as .args.

Solutions

  1. Reproduce the failing command manually (the exception's args tuple contains str(cmd)) and read the real stderr.
  2. Fix the underlying git/remote issue: configure credentials, fix the proxy, or correct the SHA.
  3. Ensure the working tree is clean (git status) and the upstream remote URL is reachable (git ls-remote).
  4. Patch the raise to use '%' formatting so the error message is actionable: raise Exception('%s: exit status %d' % (str(cmd), p.returncode)).

Example fix

// before
def RunCmd(cmd):
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
    (stdoutdata, stderrdata) = p.communicate()
    if p.returncode != 0:
        raise Exception('%s: exit status %d', str(cmd), p.returncode)
    return stdoutdata.decode('utf-8')
// after
def RunCmd(cmd):
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    (stdoutdata, stderrdata) = p.communicate()
    if p.returncode != 0:
        raise Exception('%s: exit status %d: %s' % (
            str(cmd), p.returncode, stderrdata.decode('utf-8', 'replace')))
    return stdoutdata.decode('utf-8')
Defensive patterns

Strategy: try-catch

Validate before calling

import subprocess
def check_cmd(cmd):
    p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    if p.returncode != 0:
        raise RuntimeError(f'{cmd} failed: {p.stderr.decode("utf-8","replace")}')
    return p.stdout.decode('utf-8')
# call this instead of RunCmd for actionable errors

Type guard

def cmd_will_succeed(cmd) -> bool:
    import subprocess
    p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    return p.returncode == 0

Try / catch

try:
    out = RunCmd(cmd)
except Exception as e:
    # e.args is a tuple (fmt, cmd, code) because of the formatting bug;
    # re-run with stderr to diagnose
    import subprocess
    diag = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    raise RuntimeError(f'{cmd} -> {diag.returncode}: {diag.stderr.decode("utf-8","replace")}') from e

Prevention

When it happens

Trigger: Running python roll.py --upstream-sha <sha> when a git command it invokes (clone/fetch/checkout) fails because of network, auth, missing remote, or a non-existent SHA. Any external command in the roll pipeline (git, formatting tools, file operations) exiting non-zero. Running roll.py outside a clean checkout (CheckRepoIsClean raises separately).

Common situations: CI/developer machine lacks credentials to fetch the upstream inspector_protocol repo. The target SHA was force-pushed away. Local modifications to the vendored copy block git operations. Behind a corporate proxy that breaks git fetch. Wrong CWD (the script does os.chdir inside CheckRepoIsClean, masking path issues).

Related errors


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

Appendix: source

Thrown at tools/inspector_protocol/roll.py:37

    'BUILD.gn',
    'check_protocol_compatibility.py',
    'code_generator.py',
    'concatenate_protocols.py',
    'convert_protocol_to_json.py',
    'inspector_protocol.gni',
    'README.md',
    'LICENSE',
    'pdl.py',
]

REVISION_LINE_PREFIX = 'Revision: '

def RunCmd(cmd):
  p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
  (stdoutdata, stderrdata) = p.communicate()
  if p.returncode != 0:
    raise Exception('%s: exit status %d', str(cmd), p.returncode)
  return stdoutdata.decode('utf-8')


def CheckRepoIsClean(path):
  os.chdir(path)  # As a side effect this also checks for existence of the dir.
  # If path isn't a git repo, this will throw and exception.
  # And if it is a git repo and 'git status' has anything interesting to say,
  # then it's not clean (uncommitted files etc.)
  if len(RunCmd(['git', 'status', '--porcelain'])) != 0:
    raise Exception('%s is not a clean git repo (run git status)' % path)


def CheckRepoIsInspectorProtocolCheckout(path):
  os.chdir(path)
  revision = RunCmd(['git', 'config', '--get', 'remote.origin.url']).strip()
  if (revision != 'https://chromium.googlesource.com/deps/inspector_protocol.git'):
    raise Exception('%s is not a proper inspector_protocol checkout: %s' % (path, revision))

View on GitHub (pinned to 1b2de5e052)