JuliusBrussee/caveman · error
cannot safely launch non-Node Windows command shim
Error message
cannot safely launch non-Node Windows command shim: ${executable}; install a native .exe What it means
When a Windows `.cmd`/`.bat` shim passes the size/type check, `portableInvocation` reads it and attempts to parse it as a Node shim script via `parseWindowsNodeShim`. If the parse yields nothing (packages/pi-extension/src/portable-command.ts:88), the shim is not a recognized Node launcher, and launching it would require invoking cmd.exe — which the library refuses — so it throws and suggests installing a native .exe instead.
Solutions
- Install a native .exe build of the tool so no shim parsing is needed.
- Point the configuration at the underlying JS entry point via node directly instead of the .cmd shim.
- Regenerate the shim with the standard npm installer so it matches the expected Node shim format.
Example fix
// before
portableInvocation('my-custom-wrapper.cmd', args) // hand-written batch
// after
portableInvocation(process.execPath, [nodeModules('my-cli/bin/cli.js'), ...args]) Defensive patterns
Strategy: fallback
Validate before calling
import { readFileSync } from 'node:fs';
function looksLikeNodeShim(p: string) {
try { return /node(?:\.exe)?/i.test(readFileSync(p, 'utf8')); } catch { return false; }
} Type guard
const isNodeShimFile = (p: string): boolean => {
try { const s = readFileSync(p, 'utf8'); return s.includes('node') && s.length <= 256 * 1024; } catch { return false; }
}; Try / catch
try {
const inv = portableInvocation(command, args, env);
} catch (e) {
if (e instanceof Error && /non-Node Windows command shim/.test(e.message)) {
// fall back to invoking the JS entry point with node directly
return portableInvocation(process.execPath, [resolveJsEntry(command), ...args]);
}
throw e;
} Prevention
- Install tools that ship native .exe launchers on Windows.
- Avoid hand-written .cmd wrappers for commands this library must launch.
- Use standard npm-installed shims, which follow the recognized Node shim format.
When it happens
Trigger: A `.cmd`/`.bat` shim whose content does not match the expected Node shim pattern (e.g. a hand-written batch file, a shim for a non-Node CLI, a shim format from a different package manager).
Common situations: Custom batch wrappers on PATH; CLIs installed via vendors that emit non-Node .cmd launchers; heavily customized npm global shims.
Understand the failure class
Background: "unsupported platform" / "not supported on this platform" errors: what they mean and how to fix them — this error's family across 47 libraries.
Related errors
- cannot safely launch Windows command shim
- Windows command shim target is missing
- agent conformance fixtures must cover claude and pi
- cannot safely launch non-Node Windows command shim
- cannot safely launch non-Node Windows command shim
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/ac1cbd6c593ecf32.
Report an issue: GitHub.
Appendix: source
Thrown at packages/pi-extension/src/portable-command.ts:88
// Fail-open by contract: the hook bridge treats a throw the same as a spawn
// error, and an unresolvable command falls through to the next candidate, so a
// shim we cannot parse must never take down the caller.
export function portableInvocation(
command: string,
args: readonly string[],
platform: NodeJS.Platform = process.platform,
env: NodeJS.ProcessEnv = process.env,
): PortableInvocation {
if (platform !== "win32") return { command, args: [...args] };
const executable = resolveWindowsCommand(command, env) ?? command;
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 shimScript = parseWindowsNodeShim(readFileSync(executable, "utf8"));
if (!shimScript) {
throw new Error(`cannot safely launch non-Node Windows command shim: ${executable}; install a native .exe`);
}
const script = /^[A-Za-z]:[\\/]/.test(shimScript)
? shimScript
: resolve(dirname(executable), ...shimScript.split(/[\\/]+/));
if (!statSync(script).isFile()) {
throw new Error(`Windows command shim target is missing: ${script}`);
}
return { command: process.execPath, args: [script, ...args] };
}
View on GitHub (pinned to 3ee70a1026)