{"record":{"id":"61de1f8806b6c63d","repo":"nodejs/node","slug":"s-exit-status-d","errorCode":null,"errorMessage":"%s: exit status %d","messagePattern":"(.+?): exit status (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"tools/inspector_protocol/roll.py","lineNumber":37,"sourceCode":"\n    'BUILD.gn',\n    'check_protocol_compatibility.py',\n    'code_generator.py',\n    'concatenate_protocols.py',\n    'convert_protocol_to_json.py',\n    'inspector_protocol.gni',\n    'README.md',\n    'LICENSE',\n    'pdl.py',\n]\n\nREVISION_LINE_PREFIX = 'Revision: '\n\ndef RunCmd(cmd):\n  p = subprocess.Popen(cmd, stdout=subprocess.PIPE)\n  (stdoutdata, stderrdata) = p.communicate()\n  if p.returncode != 0:\n    raise Exception('%s: exit status %d', str(cmd), p.returncode)\n  return stdoutdata.decode('utf-8')\n\n\ndef CheckRepoIsClean(path):\n  os.chdir(path)  # As a side effect this also checks for existence of the dir.\n  # If path isn't a git repo, this will throw and exception.\n  # And if it is a git repo and 'git status' has anything interesting to say,\n  # then it's not clean (uncommitted files etc.)\n  if len(RunCmd(['git', 'status', '--porcelain'])) != 0:\n    raise Exception('%s is not a clean git repo (run git status)' % path)\n\n\ndef CheckRepoIsInspectorProtocolCheckout(path):\n  os.chdir(path)\n  revision = RunCmd(['git', 'config', '--get', 'remote.origin.url']).strip()\n  if (revision != 'https://chromium.googlesource.com/deps/inspector_protocol.git'):\n    raise Exception('%s is not a proper inspector_protocol checkout: %s' % (path, revision))\n","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/inspector_protocol/roll.py#L19-L55","documentation":"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.","triggerScenarios":"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).","commonSituations":"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).","solutions":["Reproduce the failing command manually (the exception's args tuple contains str(cmd)) and read the real stderr.","Fix the underlying git/remote issue: configure credentials, fix the proxy, or correct the SHA.","Ensure the working tree is clean (git status) and the upstream remote URL is reachable (git ls-remote).","Patch the raise to use '%' formatting so the error message is actionable: raise Exception('%s: exit status %d' % (str(cmd), p.returncode))."],"exampleFix":"// before\ndef RunCmd(cmd):\n    p = subprocess.Popen(cmd, stdout=subprocess.PIPE)\n    (stdoutdata, stderrdata) = p.communicate()\n    if p.returncode != 0:\n        raise Exception('%s: exit status %d', str(cmd), p.returncode)\n    return stdoutdata.decode('utf-8')\n// after\ndef RunCmd(cmd):\n    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n    (stdoutdata, stderrdata) = p.communicate()\n    if p.returncode != 0:\n        raise Exception('%s: exit status %d: %s' % (\n            str(cmd), p.returncode, stderrdata.decode('utf-8', 'replace')))\n    return stdoutdata.decode('utf-8')","handlingStrategy":"try-catch","validationCode":"import subprocess\ndef check_cmd(cmd):\n    p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n    if p.returncode != 0:\n        raise RuntimeError(f'{cmd} failed: {p.stderr.decode(\"utf-8\",\"replace\")}')\n    return p.stdout.decode('utf-8')\n# call this instead of RunCmd for actionable errors","typeGuard":"def cmd_will_succeed(cmd) -> bool:\n    import subprocess\n    p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n    return p.returncode == 0","tryCatchPattern":"try:\n    out = RunCmd(cmd)\nexcept Exception as e:\n    # e.args is a tuple (fmt, cmd, code) because of the formatting bug;\n    # re-run with stderr to diagnose\n    import subprocess\n    diag = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n    raise RuntimeError(f'{cmd} -> {diag.returncode}: {diag.stderr.decode(\"utf-8\",\"replace\")}') from e","preventionTips":["Patch RunCmd to format with '%' and to capture stderr - the current raise hides the cause.","Run roll.py only in a clean git checkout with upstream access configured.","Verify git ls-remote <upstream> succeeds before invoking roll.py.","In CI, run roll.py in a fresh clone to avoid local-state surprises."],"tags":["inspector-protocol","roll","subprocess","git","devtools-protocol"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}