JuliusBrussee/caveman · error · Error
cannot safely launch Windows command shim
Error message
cannot safely launch Windows command shim: ${executable} What it means
On Windows, portableProcessInvocation resolves a .cmd/.bat shim for the requested command and refuses to launch it if the resolved file is not a regular file or exceeds 256 KB. This guard prevents executing oversized or non-file payloads (a known .cmd injection/abuse vector on Windows) when spawning a subprocess. The message includes the offending executable path.
Solutions
- Inspect the reported path: delete or replace the oversized/corrupt shim in node_modules/.bin (e.g. reinstall the package: npm ci) so a normal-sized .cmd is regenerated.
- Invoke the underlying tool directly (e.g. call node <script> or the .js entry) instead of going through the .cmd shim.
- Check PATH order to ensure the intended shim (not a bloated shadow copy) is resolved first: where <command> on Windows.
- If the shim is legitimately large, launch its target yourself rather than through this library — the 256 KB cap is deliberate and not configurable.
Example fix
// before (oversized npx.cmd on PATH)
spawn("npx", ["eslint"]); // throws
// after
spawn(process.execPath, [require.resolve("eslint/bin/eslint.js")]); Defensive patterns
Strategy: fallback
Validate before calling
import { statSync } from "node:fs";
function shimIsSafe(executable) {
if (!/\.(?:cmd|bat)$/i.test(executable)) return true;
const s = statSync(executable);
return s.isFile() && s.size <= 256 * 1024;
}
// check before spawning on win32 Try / catch
try {
child = portableProcessInvocation(command, args, env, process.platform);
} catch (e) {
if (e.message.startsWith("cannot safely launch")) {
child = { command: process.execPath, args: [directEntryScript, ...args] }; // bypass shim
} else throw e;
} Prevention
- Keep node_modules/.bin shims stock — regenerate with npm ci if anything rewrote them.
- Audit PATH so untrusted directories cannot shadow npm shims with oversized batch files.
- Prefer invoking node <entry.js> directly on Windows instead of .cmd shims in automated pipelines.
When it happens
Trigger: Spawning a command on win32 whose resolveWindowsCommand() result is a .cmd or .bat file that is >256 KB or statSync reports not isFile() (e.g. a directory or symlink target shaped oddly).
Common situations: A PATH shadowed by a bloated or malicious .cmd shim; a node_modules/.bin shim generated unusually large; a shim path resolving to a directory; an environment where the "command" resolves to something like npx.cmd that grew past the size cap.
Related errors
- cannot safely launch Windows command shim
- cannot safely launch non-Node Windows command shim
- cannot safely launch non-Node Windows command shim
- cannot safely launch non-Node Windows command shim
- cannot safely launch Windows command shim
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/1b19e490782dca24.
Report an issue: GitHub.
Appendix: source
Thrown at packages/subagent-tax/lib/process-tree.mjs:53
const match = line.match(/"%(?:dp0%|~dp0)\\([^"\r\n]+\.(?:cjs|mjs|js))"\s+%\*/i)
|| line.match(/"([A-Za-z]:[\\/][^"\r\n]+\.(?:cjs|mjs|js))"\s+%\*/i);
if (match) return match[1];
}
return null;
}
export function portableProcessInvocation(
command,
args,
{ platform = process.platform, env = process.env, execPath = process.execPath } = {},
) {
if (platform !== "win32") return { command, args: [...args] };
const executable = resolveWindowsCommand(command, env);
if (!executable) throw Object.assign(new Error(`command not found: ${command}`), { code: "ENOENT" });
if (!/\.(?:cmd|bat)$/i.test(executable)) return { command: executable, args: [...args] };
const stat = statSync(executable);
if (!stat.isFile() || stat.size > 256 * 1024) {
throw new Error(`cannot safely launch Windows command shim: ${executable}`);
}
const relativeScript = parseWindowsNodeShim(readFileSync(executable, "utf8"));
if (!relativeScript) {
throw new Error(`cannot safely launch non-Node Windows command shim: ${executable}`);
}
const script = /^[A-Za-z]:[\\/]/.test(relativeScript)
? relativeScript
: resolve(dirname(executable), ...relativeScript.split(/[\\/]+/));
if (!statSync(script).isFile()) throw new Error(`Windows command shim target is missing: ${script}`);
return { command: execPath, args: [script, ...args] };
}
export function harnessSpawnOptions(platform = process.platform) {
return {
detached: platform !== "win32",
windowsHide: true,
};
}View on GitHub (pinned to 3ee70a1026)