JuliusBrussee/caveman · error

Windows command shim target is missing: ${script}

Error message

Windows command shim target is missing: ${script}

What it means

Thrown by portableProcessInvocation() on win32 when the .cmd/.bat shim parsed as a Node wrapper, but the relative script path it references does not resolve to an existing file. This catches broken installs where the launcher survived but the package payload (the actual .js entry point) is missing — failing before spawn instead of producing a confusing Node MODULE_NOT_FOUND later.

Source

Thrown at packages/subagent-tax/lib/process-tree.mjs:56

export function portableProcessInvocation(
  command,
  args,
  { platform = process.platform, env = process.env, execPath = process.execPath } = {},
) {
  if (platform !== "win32") return { command, args: [...args] };
  const executable = resolveWindowsCommand(command, env);
  if (!executable) throw Object.assign(new Error(`command not found: ${command}`), { code: "ENOENT" });
  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: execPath, args: [script, ...args] };
}

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

export function forceKillTree(
  child,
  {
    platform = process.platform,
    kill = process.kill,
    taskkill = spawnSync,
  } = {},
) {

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Check whether the script path named in the error exists; if not, reinstall the owning package (delete node_modules and lockfile-consistent reinstall).
  2. If an old shim on a global PATH points into a relocated/deleted project, remove that stale shim from PATH or delete the stale file.
  3. Verify with `where <command>` that PATH precedence picks the shim inside the current install, not a leftover one elsewhere.

Example fix

# before: stale global shim points at deleted checkout
# C:\Users\me\AppData\...\my-cli.cmd -> ..\..\gone-project\node_modules\...\cli.js (missing)

# after
npm uninstall -g my-cli && npm install -g my-cli@latest
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync } from "node:fs";
// before invoking, confirm the shim's target exists (mirror of the resolver):
function shimTargetExists(shimPath) {
  const m = /node_modules[^"']*/.exec(readFileSync(shimPath, "utf8"));
  return m ? existsSync(resolve(dirname(shimPath), m[0])) : false;
}

Try / catch

try {
  const inv = portableProcessInvocation(cmd, args, { platform: "win32" });
} catch (err) {
  if (/shim target is missing/.test(err.message)) {
    // prompt reinstall of the owning package, then retry once
  } else throw err;
}

Prevention

When it happens

Trigger: Calling portableProcessInvocation with platform win32 where the shim's %~dp0-relative target (e.g. "..\\node_modules\\@scope\\pkg\\bin.js") was deleted, moved, or never installed — typically after a partial npm install, a manual node_modules prune, or moving the project directory without reinstalling.

Common situations: node_modules copied or synced incompletely; package upgraded and its bin path changed while an old shim remained on PATH; project moved to a different path breaking absolute references inside a generated shim.

Related errors


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