JuliusBrussee/caveman · error

Windows command shim target is missing: ${script}

Error message

Windows command shim target is missing: ${script}

What it means

getSpawnInvocation() parsed a Node-launching .cmd/.bat shim successfully but the .js/.cjs/.mjs target it forwards to (resolved next to the shim) does not exist as a file. The launcher refuses to guess and throws before spawning.

Source

Thrown at src/mcp-servers/caveman-shrink/spawn-options.js:54

    }
  }
  return null;
}

function getSpawnInvocation(command, args, platform = process.platform, 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 source = fs.readFileSync(executable, 'utf8');
  let relativeScript = null;
  for (const line of source.split(/\r?\n/)) {
    if (!/(?:\bnode(?:\.exe)?\b|_prog)/i.test(line) || !/%\*/.test(line)) continue;
    const match = line.match(/"%(?:dp0%|~dp0)\\([^"\r\n]+\.(?:cjs|mjs|js))"\s+%\*/i);
    if (match) { relativeScript = match[1]; break; }
  }
  if (!relativeScript) throw new Error(`cannot safely launch non-Node Windows command shim: ${executable}`);
  const script = path.resolve(path.dirname(executable), ...relativeScript.split(/[\\/]+/));
  if (!fs.statSync(script).isFile()) throw new Error(`Windows command shim target is missing: ${script}`);
  return { command: process.execPath, args: [script, ...args] };
}

function getSpawnOptions(platform = process.platform) {
  return {
    stdio: ['pipe', 'pipe', 'inherit'],
    windowsHide: true,
  };
}

module.exports = { getSpawnInvocation, getSpawnOptions, resolveWindowsCommand };

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Delete node_modules and do a clean, lockfile-faithful install (npm ci) of the package providing the shim
  2. Verify the printed target path in the error actually exists; if it is wrong the shim is stale — reinstall
  3. Bypass the shim: invoke node directly on the package's real entrypoint

Example fix

# before (broken partial install)
# Windows command shim target is missing: ...\node_modules\.bin\..\foo\cli.js

# after
npm ci   # or: rm -rf node_modules && npm install
Defensive patterns

Strategy: fallback

Validate before calling

const fs = require("node:fs");
const path = require("node:path");
// before spawning via a .cmd shim on Windows, confirm its JS target exists
function shimTargetExists(shimPath) {
  const m = fs.readFileSync(shimPath, "utf8").match(/"%(?:dp0%|~dp0)\\([^"\r\n]+\.(?:cjs|mjs|js))"\s+%\*/i);
  return m ? fs.statSync(path.resolve(path.dirname(shimPath), m[1])).isFile() : false;
}

Try / catch

try {
  spawn(invocation.command, invocation.args);
} catch (e) {
  if (/shim target is missing/.test(String(e?.message))) {
    await reinstallPackage();
    spawn(process.execPath, [knownEntry, ...args]);
  } else throw e;
}

Prevention

When it happens

Trigger: A partial npm install where the shim landed but the package JS did not; the package was updated or moved and the shim is stale; node_modules pruned by a dependency cleaner while .bin shims remained.

Common situations: Interrupted npm or pnpm installs; npm prune/autoclean leaving orphaned .bin entries; copying node_modules between machines or paths where the relative %~dp0 target breaks; package hoisting quirks in monorepos.

Related errors


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