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
- Run the CLI's build step so caveman-pi-extension.mjs is emitted next to the module
- Set CAVEMAN_PI_EXTENSION to the absolute path of an existing extension file (verify with ls)
- Reinstall/upgrade the CLI package so bundled assets are present
- 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
- Always run the build step after cloning or upgrading the CLI
- In CI, cache build outputs alongside node_modules
- If overriding via CAVEMAN_PI_EXTENSION, assert the file exists in setup scripts
- Prefer full package installs over trimmed/pruned asset installs
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
- adapter-specific build_sha256 values must differ
- agent conformance fixtures must cover claude and pi
- agent conformance mismatch
- MCP transaction lost one durable journal copy; refusing…
- bedrock Mantle endpoint is not enabled
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)