JuliusBrussee/caveman · error · Error

cannot safely launch non-Node Windows command shim: ${execut

Error message

cannot safely launch non-Node Windows command shim: ${executable}

What it means

After a Windows .cmd/.bat shim passes the size and file-type checks, the library parses its contents to confirm it is a Node shim (the standard npm-generated wrapper that forwards to a target .js file). Only verified Node shims are safe to relaunch via process.execPath, because executing arbitrary batch content would allow command injection. If the shim body does not match the known Node-shim shape, it throws 'cannot safely launch non-Node Windows command shim'.

Source

Thrown at packages/agent/src/portable-process.ts:60

  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",
      args: ["/d", "/s", "/c", source],
    };
  }
  return { command: "/bin/sh", args: ["-c", source] };

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Replace the custom .cmd with an npm-style Node shim, or invoke node <script.js> directly and skip the .cmd entirely
  2. Pin/align the package manager so shims are generated in the recognized format
  3. Check PATH resolution: resolveWindowsCommand may be finding a different .cmd than you expect (where <command> in cmd.exe)
  4. For non-Node CLIs, spawn via an explicit shell invocation (hostShellInvocation) rather than the portable path

Example fix

// before
const inv = portableInvocation("build-tool", args); // build-tool.cmd is a hand-written batch script

// after
const inv = portableInvocation(process.execPath, ["./tools/build-tool.js", ...args]);
Defensive patterns

Strategy: fallback

Try / catch

try {
  const inv = portableInvocation(cmd, args);
} catch (e) {
  if (e instanceof Error && e.message.includes("non-Node Windows command shim")) {
    // fallback: invoke the underlying script or an explicit shell command
    return hostShellInvocation(`${cmd} ${args.join(" ")}`, "win32", env);
  }
  throw e;
}

Prevention

When it happens

Trigger: A .cmd/.bat that is a hand-written batch script (if/for/set logic) rather than an npm Node shim, a shim format from a different tool (pnpm/yarn generate different wrappers unless recognized), or an edited/obfuscated shim whose target line no longer parses.

Common situations: Team ships a custom .cmd wrapper for an internal CLI, a package manager upgrade changed shim generation format, or the command name collides with a system batch file found earlier on PATH.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/0638a2a8ecc80289. Report an issue: GitHub.