JuliusBrussee/caveman · error

cannot safely launch Windows command shim

Error message

cannot safely launch Windows command shim: ${executable}

What it means

On Windows, `portableInvocation` in packages/pi-extension/src/portable-command.ts:84 safely wraps .cmd/.bat shims (to avoid spawning via cmd.exe). Before parsing a shim it stats the resolved executable and throws this Error if the path is not a regular file or exceeds 256 KiB — a guard against unsafe or corrupt command shims.

Solutions

  1. Reinstall the CLI whose shim is corrupt (`npm install -g <pkg>` or local reinstall) to regenerate a clean .cmd.
  2. Ensure the command resolves to the intended shim: check PATH order and remove shadowing directories/files.
  3. Prefer a native .exe binary for the command so the shim path is not taken at all.

Example fix

// before
npm i -g some-cli   // leaves a corrupt some-cli.cmd
// after
cache clean + reinstall
npm cache clean --force && npm i -g some-cli
Defensive patterns

Strategy: fallback

Validate before calling

import { statSync } from 'node:fs';
function isLaunchableShim(p: string) {
  try { const s = statSync(p); return s.isFile() && s.size <= 256 * 1024; } catch { return false; }
}

Type guard

const isRegularFile = (p: string): boolean => { try { return statSync(p).isFile(); } catch { return false; } };

Try / catch

try {
  const inv = portableInvocation(command, args, env);
} catch (e) {
  if (e instanceof Error && /cannot safely launch Windows command shim/.test(e.message)) {
    // fall back to a native binary or reinstall the tool
    throw new Error(`Reinstall ${command} or provide a native .exe: ${e.message}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Resolving a Windows command to a `.cmd`/`.bat` file that does not exist (statSync would throw ENOENT first) — practically: the shim path exists but is a directory, a device/special file, or a bloated (>256KB) file.

Common situations: A broken npm install leaving a corrupt or oversized .cmd shim; a directory named like a shim earlier on PATH; node_modules path shadowing.

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


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/c5a8faa28e21ec63. Report an issue: GitHub.

Appendix: source

Thrown at packages/pi-extension/src/portable-command.ts:84

// Returns the command/args pair to hand to execFile/spawn. Non-win32 is a
// pass-through, so this is safe to route every invocation through.
//
// 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)