JuliusBrussee/caveman · error · 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

Thrown by portableInvocation() on Windows when a .cmd/.bat shim passes the size/file checks but its contents are not a recognizable Node shim (parseWindowsNodeShim returns null). The library will not execute arbitrary batch shims; it demands either a native .exe or a shim whose embedded script points at a Node entry it can launch with process.execPath. This avoids executing untrusted batch logic with user-supplied args.

Solutions

  1. Install a native .exe build of the command (vendor installer, winget, scoop) and ensure it wins on PATH.
  2. Replace the custom .cmd wrapper with the standard npm-generated Node shim by reinstalling the package.
  3. Invoke the target directly: `node path\to\cli.js` for Node tools, bypassing the shim.
  4. Remove the unknown .cmd from PATH if it is not the tool you intend to run.

Example fix

// before
invocation("mytool") // mytool resolves to custom mytool.cmd batch script -> throws
// after
// install native binary:
winget install mytool
invocation("mytool") // resolves to mytool.exe -> spawns directly
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync, statSync } from "fs";
function looksLikeNodeShim(p: string): boolean {
  if (!/\.(?:cmd|bat)$/i.test(p)) return true;
  try {
    const content = readFileSync(p, "utf8");
    return /node/i.test(content) && /%~dp0|node_modules/.test(content); // standard npm shim markers
  } catch { return false; }
}

Try / catch

try {
  const inv = invocation(cmd, args);
  spawn(inv.command, inv.args);
} catch (e) {
  if (e instanceof Error && e.message.includes("non-Node Windows command shim")) {
    console.error("Custom .cmd wrapper rejected; install the native .exe or run node <cli.js> directly");
  } else throw e;
}

Prevention

When it happens

Trigger: platform === 'win32', the resolved command ends in .cmd/.bat, and reading the file yields content that parseWindowsNodeShim cannot interpret as a Node launcher shim (e.g. a plain batch script, not the standard npm/node shim format).

Common situations: Third-party tools shipping hand-written .cmd wrappers, corporate wrapper scripts around real binaries, globally-installed tools whose shims were customized or corrupted, or a text file coincidentally named .cmd on PATH.

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

Appendix: source

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

  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)