JuliusBrussee/caveman · error

Windows command shim target missing: ${script}

Error message

Windows command shim target missing: ${script}

What it means

portableInvocation() resolved the Node script that a Windows .cmd/.bat shim forwards to, but statSync says that target is not an existing file. The safe-spawn path will not fall back to executing the raw batch file, so it fails fast with the resolved absolute script path in the message.

Source

Thrown at agents/delegate/portable-process.mjs:43

  }
  return null;
}

export function portableInvocation(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 stat = statSync(executable);
  if (!stat.isFile() || stat.size > 256 * 1024) throw new Error(`unsafe Windows command shim: ${executable}`);
  let relativeScript = null;
  for (const line of readFileSync(executable, "utf8").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(`non-Node Windows command shim: ${executable}`);
  const script = resolve(dirname(executable), ...relativeScript.split(/[\\/]+/));
  if (!statSync(script).isFile()) throw new Error(`Windows command shim target missing: ${script}`);
  return { command: process.execPath, args: [script, ...args] };
}

export function delegateSpawnOptions(platform = process.platform) {
  return {
    detached: platform !== "win32",
    windowsHide: true,
  };
}

export async function killProcessTree(
  child,
  platform = process.platform,
  taskkill = spawnSync,
  kill = process.kill,
  graceMs = 1500,
) {
  if (!child?.pid) return;

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Do a clean reinstall of the package owning the shim (rm -rf node_modules plus a lockfile-exact install)
  2. Check the printed absolute path — if it looks truncated or wrong-shaped, fix PATH length or Long Paths settings and reinstall
  3. Invoke the real entrypoint with node directly, bypassing the shim

Example fix

# before
# Windows command shim target missing: C:\proj\node_modules\.bin\..\foo\dist\cli.js

# after
npm ci   # clean, lockfile-exact reinstall restores the target file
Defensive patterns

Strategy: fallback

Validate before calling

// before spawn: probe the shim's forwarded target
const ok = shimTargetExists(cmd); // see error 464 validationCode
if (!ok) await cleanReinstall(pkg);

Try / catch

try { spawn(inv.command, inv.args); }
catch (e) {
  if (/shim target (?:is )?missing/.test(String(e?.message))) { await reinstall(pkg); }
  else throw e;
}

Prevention

When it happens

Trigger: A stale or half-installed package: the .bin shim exists but the JS entrypoint it points to was deleted, moved, or never extracted; broken %~dp0 resolution after node_modules was relocated.

Common situations: Interrupted installs; aggressive node_modules cleaners (npkill, dedupe tools); monorepo hoisting moving the package while leaving the workspace .bin shim; Windows path-length limits truncating extraction.

Related errors


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