JuliusBrussee/caveman · error · Error
cannot safely launch Windows command shim: ${executable}
Error message
cannot safely launch Windows command shim: ${executable} What it means
On win32, portable invocation resolves .cmd/.bat shims (the wrappers npm creates for bin scripts) and inspects them before executing, because spawning a .cmd directly lets attackers inject arbitrary commands via the shim body. If the resolved file is not a regular file or exceeds 256 KiB, the library refuses to launch it with 'cannot safely launch Windows command shim'. This is a deliberate mitigation for CVE-2024-27980-class attacks.
Source
Thrown at packages/agent/src/portable-process.ts:56
}
export function portableInvocation(
command: string,
args: readonly string[],
options: {
platform?: NodeJS.Platform;
env?: NodeJS.ProcessEnv;
execPath?: string;
} = {},
): PortableInvocation {
const platform = options.platform ?? process.platform;
const env = options.env ?? process.env;
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 relativeScript = parseWindowsNodeShim(readFileSync(executable, "utf8"));
if (!relativeScript) {
throw new Error(`cannot safely launch non-Node Windows command shim: ${executable}`);
}
const script = resolve(dirname(executable), ...relativeScript.split(/[\\/]+/));
if (!statSync(script).isFile()) throw new Error(`Windows command shim target is missing: ${script}`);
return { command: options.execPath ?? process.execPath, args: [script, ...args] };
}
export function hostShellInvocation(
source: string,
platform: NodeJS.Platform = process.platform,
env: NodeJS.ProcessEnv = process.env,
): PortableInvocation {
if (platform === "win32") {
return {
command: envValue(env, "ComSpec") ?? "cmd.exe",View on GitHub (pinned to 27d5a3981a)
Solutions
- Reinstall the package that owns the shim so npm regenerates a sane, small wrapper: npm reinstall <pkg> (or delete node_modules/.bin/<name> and npm install)
- Verify the target: it must be a regular file well under 256 KiB
- Invoke the underlying node script (the shim's target) directly instead of the .cmd
- If you control the shim, trim it — legitimate npm shims are a few hundred bytes
Example fix
// before
const inv = portableInvocation("my-tool", []); // my-tool.cmd is 300 KiB or not a regular file
// after
// reinstall to restore the small generated shim
// $ npm install my-tool --force
const inv = portableInvocation("my-tool", []); Defensive patterns
Strategy: try-catch
Validate before calling
import { statSync } from "node:fs";
function isLaunchableShim(executable: string): boolean {
if (!/\.(?:cmd|bat)$/i.test(executable)) return true;
try {
const s = statSync(executable);
return s.isFile() && s.size <= 256 * 1024;
} catch {
return false;
}
} Try / catch
try {
const inv = portableInvocation(cmd, args);
} catch (e) {
if (e instanceof Error && e.message.startsWith("cannot safely launch Windows command shim")) {
// reinstall the owning package or fall back to node <script.js> directly
}
throw e;
} Prevention
- Prefer spawning node <script.js> directly over .cmd wrappers on Windows
- Run npm install cleanly in CI rather than caching partial .bin directories
- Treat oversized or non-file shims as tampering: investigate instead of bypassing
When it happens
Trigger: Running on Windows (or with options.platform forced to "win32") where the command resolves to a .cmd/.bat that is actually a directory, a symlink/pipe rather than a regular file, or a batch file larger than 256 KiB.
Common situations: A corrupted or hand-edited npm shim grew beyond the size cap, an antivirus quarantine left a stub, a symlinked global bin directory, or tests that fabricate oversized fake .cmd files to exercise this path.
Related errors
- cannot safely launch non-Node Windows command shim: ${execut
- cave_untrusted_tool_definition:${declared.name}
- cave_memory_tenant_invalid
- cave_memory_agent_invalid
- Windows command shim target is missing: ${script}
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/d5a9d74488467b9d.
Report an issue: GitHub.