JuliusBrussee/caveman · error · Error

Pi extension not found at

Error message

Pi extension not found at ${extension}; set CAVEMAN_PI_EXTENSION or run the CLI build step

What it means

When wrapping Pi-based commands, the CLI injects a bundled extension script `caveman-pi-extension.mjs` located next to the CLI module. `resolvePiExtension()` throws this error when neither the CAVEMAN_PI_EXTENSION env var nor the default build-output path points at an existing file, i.e. the extension artifact was never built or was moved.

Solutions

  1. Run the CLI's build step so caveman-pi-extension.mjs is emitted next to the module
  2. Set CAVEMAN_PI_EXTENSION to the absolute path of an existing extension file (verify with ls)
  3. Reinstall/upgrade the CLI package so bundled assets are present
  4. If the env var is set, unset it to fall back to the default path

Example fix

// before
CAVEMAN_PI_EXTENSION=./old-ext.mjs caveman ...
// after
caveman build  # emits dist/caveman-pi-extension.mjs
# or
export CAVEMAN_PI_EXTENSION=/abs/path/caveman-pi-extension.mjs
Defensive patterns

Strategy: validation

Validate before calling

const ext = process.env.CAVEMAN_PI_EXTENSION?.trim()
  || join(dirname(fileURLToPath(import.meta.url)), "caveman-pi-extension.mjs");
if (!existsSync(ext)) throw new Error(`run the build step first; expected ${ext}`);

Try / catch

try {
  await runPiWrap(args);
} catch (e) {
  if ((e as Error).message.includes("Pi extension not found")) {
    await build(); // then retry once
    await runPiWrap(args);
  } else throw e;
}

Prevention

When it happens

Trigger: Running any pi-wrapped command where: CAVEMAN_PI_EXTENSION is set to a nonexistent/misspelled path, or the default path (dirname of the CLI module + caveman-pi-extension.mjs) does not exist because the build step was skipped or artifacts were pruned (e.g. running from a source checkout or a slim package install).

Common situations: Running the CLI from a repo clone without running the build; installing a trimmed package that excludes .mjs assets; CI caching node_modules without build outputs; typo in CAVEMAN_PI_EXTENSION or stale path after an upgrade.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/34ae0da0e73116fe. Report an issue: GitHub.

Appendix: source

Thrown at packages/cli/src/index.ts:10788

}

// resolveDelegateMcpCommand locates the dependency-free caveman-delegate stdio
// server (CAVEMAN_DELEGATE_MCP override, else the copy shipped alongside the
// CLI). Null when the script is missing — callers must not register a dead entry.
function resolveDelegateMcpCommand(): { command: string; args: string[] } | null {
  const candidates = [
    process.env.CAVEMAN_DELEGATE_MCP || "",
    join(dirname(fileURLToPath(import.meta.url)), "caveman-delegate-mcp.mjs"),
  ].filter(Boolean);
  const script = candidates.find((c) => existsSync(c));
  return script ? { command: process.execPath, args: [script] } : null;
}

function resolvePiExtension(): string {
  const extension = process.env.CAVEMAN_PI_EXTENSION?.trim()
    || join(dirname(fileURLToPath(import.meta.url)), "caveman-pi-extension.mjs");
  if (!existsSync(extension)) {
    throw new Error(`Pi extension not found at ${extension}; set CAVEMAN_PI_EXTENSION or run the CLI build step`);
  }
  return extension;
}

function buildPiWrapArgs(cmdArgs: string[], env: NodeJS.ProcessEnv, gw: string): string[] {
  const extension = resolvePiExtension();
  const { cmd, pre } = cavemanInvocation();
  env.CAVEMAN_PI_HOOK_CMD = JSON.stringify([cmd, ...pre]);
  env.CAVE_GATEWAY_URL = gw;
  // The profile's injection block declares the loader flag; using it here keeps the
  // compiled registry the single source of truth for how the asset is loaded.
  const profile = AGENTS.find((a) => a.id === "pi");
  const loaderFlag = profile?.injection.method === "native-extension" ? profile.injection.loader_flag : "--extension";
  return [loaderFlag, extension, ...cmdArgs];
}

const HERMES_MCP_BEGIN = "# >>> caveman:mcp";
const HERMES_MCP_END = "# <<< caveman:mcp";

View on GitHub (pinned to 3ee70a1026)