JuliusBrussee/caveman · error · Error

cannot safely launch Windows command shim

Error message

cannot safely launch Windows command shim: ${executable}

What it means

Thrown by portableInvocation() on Windows when a resolved command resolves to a .cmd/.bat shim that is unsafe to execute directly: it is not a regular file or exceeds 256 KiB. Launching .cmd/.bat files via spawn is both a command-injection risk and unreliable for huge auto-generated npm shims, so the library refuses and requires a safe native executable. There is a sibling error for shims whose script is not a Node shim.

Solutions

  1. Install a native .exe version of the tool (e.g. via npm global with node, or the vendor's Windows installer) instead of relying on the .cmd shim.
  2. Delete and reinstall the package to regenerate a clean, small .cmd shim (`npm install -g <pkg>`).
  3. Ensure the resolved path is a regular file; remove directories/files shadowing the command on PATH.
  4. Call the underlying node script directly: `node <pkg-root>/bin/cli.js ...` instead of the shim.

Example fix

// before (Windows)
spawn("npm.cmd", ["run", "build"])   // may throw: shim unsafe (size/file)
// after
spawn("node", [require.resolve("npm/bin/npm-cli.js"), "run", "build"], { windowsHide: true })
Defensive patterns

Strategy: validation

Validate before calling

import { statSync } from "fs";
function isSafeCmdShim(p: string): boolean {
  if (!/\.(?:cmd|bat)$/i.test(p)) return true;
  try { const s = statSync(p); return s.isFile() && s.size <= 256 * 1024; }
  catch { return false; }
}
// call isSafeCmdShim(resolvedExecutable) before spawning

Try / catch

try {
  spawn(invocation(cmd, args).command, invocation(cmd, args).args);
} catch (e) {
  if (e instanceof Error && e.message.startsWith("cannot safely launch Windows command shim")) {
    console.error("Shim unsafe; reinstall the package or use its native .exe/node entry", e.message);
  } else throw e;
}

Prevention

When it happens

Trigger: platform === 'win32' and resolveWindowsCommand() resolved the command to a *.cmd/*.bat file whose statSync shows !isFile() or size > 256*1024 bytes.

Common situations: npm's npx-shim .cmd files left oversized after a broken install, a directory or corrupted file shadowing the command name on PATH, or antivirus quarantine leaving a stub .cmd.

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/11a78947a6c27b44. Report an issue: GitHub.

Appendix: source

Thrown at packages/cli/src/portable-command.ts:65

      const candidate = join(directory, name);
      if (existsSync(candidate)) return candidate;
    }
  }
  return undefined;
}

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)