JuliusBrussee/caveman · error
already exists and is not Caveman-owned; refusing to…
Error message
${extensionPath} already exists and is not Caveman-owned; refusing to overwrite it What it means
During Caveman's native Pi install, the installer writes a persistent extension file at ~/.pi/agent/extensions/caveman-native.js. If that file already exists but lacks the 'caveman:native-pi' marker, the installer refuses to overwrite it. This protects user-authored Pi extensions from being clobbered by Caveman's generated file.
Solutions
- Back up and remove (or rename) ~/.pi/agent/extensions/caveman-native.js, then re-run the Pi enable command.
- If the file is your own extension, move it to a different filename in the extensions directory.
- If it was a Caveman file whose marker was lost, run `caveman doctor pi` to detect and repair the drift.
- Re-run the enable flow to regenerate the extension.
Example fix
// before ~/.pi/agent/extensions/caveman-native.js // no 'caveman:native-pi' marker // after mv ~/.pi/agent/extensions/caveman-native.js ~/.pi/agent/extensions/caveman-native.js.bak // re-run: caveman enable -> regenerates file with '// caveman:native-pi'
Defensive patterns
Strategy: fallback
Validate before calling
import { readFileSync, existsSync } from 'fs';
const p = join(homedir(), '.pi', 'agent', 'extensions', 'caveman-native.js');
if (existsSync(p) && !readFileSync(p, 'utf8').includes('caveman:native-pi')) {
// rename or remove before enabling
} Try / catch
try {
await cavemanEnablePi();
} catch (e) {
if (String(e.message).includes('already exists and is not Caveman-owned')) {
rename(extensionPath, extensionPath + '.bak');
await cavemanEnablePi();
}
} Prevention
- Never name personal Pi extensions `caveman-native.js`.
- Keep the 'caveman:native-pi' marker comment intact in the generated file.
- Use `caveman doctor pi` after manual edits to the extensions directory.
- Back up ~/.pi/agent/extensions before enabling.
When it happens
Trigger: Running the Pi enable flow when ~/.pi/agent/extensions/caveman-native.js exists and its contents do not include the 'caveman:native-pi' marker string.
Common situations: A user already placed their own caveman-native.js extension; a file from another tool occupies the path; an older Caveman install wrote the file without the current marker; the marker comment was stripped by a minifier or edit.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- already exists and is not Caveman-owned; refusing to…
- : MCP ownership journal failed; native config may already…
- cave_live_eval_sandbox_profile_invalid
- caveman build: invalid .caveman/provider.json
- exists and is not exact Caveman-owned content; refusing to…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/e3bf476ad383c014.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/index.ts:7380
owned: { routes, previous_routes: previousRoutes, installed_mcp: installedMcp, previous_mcp: previousMcp ?? null },
},
{
file: pluginPath,
before: pluginBefore,
after: Buffer.from(opencodeNativePluginSource()),
kind: "opencode-plugin",
},
];
}
function piNativeMutations(): NativeMutation[] {
const extensionDir = join(homedir(), ".pi", "agent", "extensions");
// Pi's extension auto-discovery accepts only .ts/.js (isExtensionFile in
// pi-coding-agent 0.84.2); a .mjs here would journal fine and never load.
const extensionPath = join(extensionDir, "caveman-native.js");
const before = fileBytes(extensionPath);
if (before && !before.toString("utf8").includes("caveman:native-pi")) {
throw new Error(`${extensionPath} already exists and is not Caveman-owned; refusing to overwrite it`);
}
// Plain `pi` launches carry no wrap env, so the persistent artifact bakes in
// the invocation this enable resolved (falling back to PATH when unset) —
// same pattern as the generated opencode plugin.
const { cmd, pre } = cavemanInvocation();
const after = Buffer.concat([
Buffer.from("// caveman:native-pi — GENERATED by `caveman enable pi`.\n"),
Buffer.from(`process.env.CAVEMAN_PI_HOOK_CMD ??= ${JSON.stringify(JSON.stringify([cmd, ...pre]))};\n`),
readFileSync(resolvePiExtension()),
]);
return [{
file: extensionPath,
before,
after,
kind: "pi-extension",
owned: { sha256: bytesHash(after), created_extension_dir: !existsSync(extensionDir) },
}];
}View on GitHub (pinned to 3ee70a1026)