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

  1. Install a native .exe build of the tool so no shim parsing is needed.
  2. Point the configuration at the underlying JS entry point via node directly instead of the .cmd shim.
  3. 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

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


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)