JuliusBrussee/caveman · error
${pluginPath} already exists and is not Caveman-owned; refus
Error message
${pluginPath} already exists and is not Caveman-owned; refusing to overwrite it What it means
opencodeNativeMutations() writes a Caveman-owned plugin file (opencodeNativePluginPath(), an opencode plugin JS file) and only overwrites it when the existing content contains the "caveman:native-opencode" ownership marker. Any pre-existing file without that marker is treated as user-owned and protected from overwrite.
Source
Thrown at packages/cli/src/index.ts:6352
for (const [providerID, route] of Object.entries(routes)) {
const provider = providers[providerID] && typeof providers[providerID] === "object" && !Array.isArray(providers[providerID]) ? providers[providerID] as Record<string, unknown> : {};
const options = provider.options && typeof provider.options === "object" && !Array.isArray(provider.options) ? provider.options as Record<string, unknown> : {};
previousRoutes[providerID] = options.baseURL ?? null;
options.baseURL = route;
provider.options = options;
providers[providerID] = provider;
}
root.provider = providers;
const mcp = root.mcp && typeof root.mcp === "object" && !Array.isArray(root.mcp) ? root.mcp as Record<string, unknown> : {};
const previousMcp = mcp.caveman;
const installedMcp = { type: "local", command: [mcpBinary], enabled: true };
mcp.caveman = installedMcp;
root.mcp = mcp;
const pluginPath = opencodeNativePluginPath();
const pluginBefore = fileBytes(pluginPath);
if (pluginBefore && !pluginBefore.toString("utf8").includes("caveman:native-opencode")) {
throw new Error(`${pluginPath} already exists and is not Caveman-owned; refusing to overwrite it`);
}
return [
{
file: configPath,
before,
after: Buffer.from(JSON.stringify(root, null, 2) + "\n"),
kind: "opencode-config",
owned: { routes, previous_routes: previousRoutes, installed_mcp: installedMcp, previous_mcp: previousMcp ?? null },
},
{
file: pluginPath,
before: pluginBefore,
after: Buffer.from(opencodeNativePluginSource()),
kind: "opencode-plugin",
},
];
}
View on GitHub (pinned to 27d5a3981a)
Solutions
- Open the plugin file, confirm it is not something you need, and move it aside (e.g. plugin.js.bak) or delete it
- If it came from an old caveman install, delete it so the current version rewrites it with the current marker
- Re-run the opencode native install; it will now create the file with proper ownership marker
Example fix
# before $ ls ~/.config/opencode/plugin/caveman.js # exists, user-authored Error: .../caveman.js already exists and is not Caveman-owned; refusing to overwrite it # after $ mv ~/.config/opencode/plugin/caveman.js ~/caveman.js.bak $ caveman native install opencode # recreates Caveman-owned plugin
Defensive patterns
Strategy: validation
Validate before calling
import { existsSync, readFileSync } from "node:fs";
function pluginFileSafeToOwn(path: string): boolean {
if (!existsSync(path)) return true;
try { return readFileSync(path, "utf8").includes("caveman:native-opencode"); }
catch { return false; }
} Try / catch
try { nativeInstallOpencode(); } catch (e) {
if (e instanceof Error && /not Caveman-owned/.test(e.message)) {
backUpAndRemovePlugin(); nativeInstallOpencode();
} else throw e;
} Prevention
- Don't hand-author files at paths installers reserve; check the tool's plugin path first
- When reusing dotfile repos, exclude tool-generated plugin files
- Never edit Caveman-owned plugin files; regenerate via the installer
When it happens
Trigger: Native opencode install when the plugin file path already exists and its contents lack the caveman:native-opencode marker string.
Common situations: User (or another tool) already placed their own plugin at that path; a leftover plugin from an incompatible/older caveman version whose marker text changed; manually created stub file.
Related errors
- ${path} already exists; refusing to overwrite an unjournaled
- ${configPath} provider must be a JSON object; refusing to ov
- ${configPath} mcp must be a JSON object; refusing to overwri
- ${corePath} exists and is not exact Caveman-owned content; r
- cave_harness_aborted
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/cec828983101d7a4.
Report an issue: GitHub.