{"record":{"id":"f5e3a040af9d3ad0","repo":"JuliusBrussee/caveman","slug":"enoent","errorCode":"ENOENT","errorMessage":"command not found: ${command}","messagePattern":"command not found: (.+?)","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/subagent-tax/lib/process-tree.mjs","lineNumber":45,"sourceCode":"}\n\nexport function parseWindowsNodeShim(source) {\n  for (const line of source.split(/\\r?\\n/)) {\n    if (!/(?:\\bnode(?:\\.exe)?\\b|_prog)/i.test(line) || !/%\\*/.test(line)) continue;\n    const match = line.match(/\"%(?:dp0%|~dp0)\\\\([^\"\\r\\n]+\\.(?:cjs|mjs|js))\"\\s+%\\*/i);\n    if (match) return match[1];\n  }\n  return null;\n}\n\nexport function portableProcessInvocation(\n  command,\n  args,\n  { platform = process.platform, env = process.env, execPath = process.execPath } = {},\n) {\n  if (platform !== \"win32\") return { command, args: [...args] };\n  const executable = resolveWindowsCommand(command, env);\n  if (!executable) throw Object.assign(new Error(`command not found: ${command}`), { code: \"ENOENT\" });\n  if (!/\\.(?:cmd|bat)$/i.test(executable)) return { command: executable, args: [...args] };\n  const stat = statSync(executable);\n  if (!stat.isFile() || stat.size > 256 * 1024) {\n    throw new Error(`cannot safely launch Windows command shim: ${executable}`);\n  }\n  const relativeScript = parseWindowsNodeShim(readFileSync(executable, \"utf8\"));\n  if (!relativeScript) {\n    throw new Error(`cannot safely launch non-Node Windows command shim: ${executable}`);\n  }\n  const script = resolve(dirname(executable), ...relativeScript.split(/[\\\\/]+/));\n  if (!statSync(script).isFile()) throw new Error(`Windows command shim target is missing: ${script}`);\n  return { command: execPath, args: [script, ...args] };\n}\n\nexport function harnessSpawnOptions(platform = process.platform) {\n  return {\n    detached: platform !== \"win32\",\n    windowsHide: true,","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/subagent-tax/lib/process-tree.mjs#L27-L63","documentation":"Thrown by portableProcessInvocation() when running on win32 and resolveWindowsCommand() cannot locate the requested command (via PATH lookup). It carries code ENOENT so callers can treat it exactly like a standard spawn ENOENT. The indirection exists because spawning .cmd/.bat shims directly is unsafe on Windows (CVE-2024-27980 class of issues), so the library resolves the shim manually before spawning.","triggerScenarios":"Calling portableProcessInvocation(\"some-cli\", args) with platform \"win32\" (or on a real Windows host) where some-cli is not installed, not on the PATH contained in the env object passed in, or is on PATH but resolveWindowsCommand's lookup rules miss it.","commonSituations":"The agent binary or harness CLI is not installed on the Windows machine; the env passed to the function was constructed from a minimal env without PATH; the command is installed for a different shell/user profile; the PATH entry uses a quoting style the resolver rejects.","solutions":["Verify the command is actually installed and on PATH: run `where <command>` in the same environment.","If you pass a custom env option, include a valid PATH (spread process.env or set env.PATH explicitly) so the resolver can find the executable.","If the command is only available via a package-manager shim directory, add that directory to PATH or pass the absolute path to the executable as command."],"exampleFix":"// before\nconst inv = portableProcessInvocation(\"claude\", [], { platform: \"win32\", env: { SYSTEM_ROOT: \"C:\" } }); // throws ENOENT\n\n// after\nconst inv = portableProcessInvocation(\"claude\", [], { platform: \"win32\", env: process.env });","handlingStrategy":"validation","validationCode":"import { accessSync, constants } from \"node:fs\";\nfunction commandResolvable(command, env, platform) {\n  if (platform !== \"win32\") return true;\n  const path = env.PATH ?? \"\";\n  return path.split(\";\").some((dir) => {\n    try { accessSync(join(dir, command + \".cmd\"), constants.X_OK); return true; }\n    catch { return false; }\n  });\n}","typeGuard":null,"tryCatchPattern":"try {\n  const inv = portableProcessInvocation(cmd, args, { platform: \"win32\" });\n} catch (err) {\n  if (err?.code === \"ENOENT\") {\n    // report 'CLI not installed / not on PATH' to the user, suggest install instructions\n  } else throw err;\n}","preventionTips":["Check command availability (`where <cmd>` on Windows) before invoking.","Always pass a full env (spread process.env) so PATH lookup works.","Prefer absolute paths to known install locations over bare command names."],"tags":["windows","spawn","enoent","path","process"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}