JuliusBrussee/caveman · error

already exists and is not Caveman-owned; refusing to…

Error message

${pluginPath} already exists and is not Caveman-owned; refusing to overwrite it

What it means

During Caveman's native opencode install, the installer writes a generated plugin file at the path returned by opencodeNativePluginPath(). Before writing, it checks any existing file for the marker string 'caveman:native-opencode'; if the file exists but lacks that marker, it refuses to overwrite a file it does not own. This prevents silently clobbering a user's hand-written opencode plugin.

Solutions

  1. Back up and delete or rename the existing plugin file, then re-run the install so Caveman can generate a fresh one.
  2. If the file is yours, move it to a different path so the Caveman-owned path is free.
  3. If you believe the file is a Caveman plugin missing its marker (e.g. edited), restore the 'caveman:native-opencode' marker line or reinstall the plugin via `caveman doctor`.
  4. Re-run the enable command after clearing the path.

Example fix

// before: existing non-Caveman plugin blocks install
~/.config/opencode/plugins/native.ts  // user content, no marker
// after: move or remove it, then re-run installer
mv ~/.config/opencode/plugins/native.ts ~/native.ts.bak
// installer now writes plugin containing: // caveman:native-opencode
Defensive patterns

Strategy: fallback

Validate before calling

import { readFileSync, existsSync } from 'fs';
const p = opencodePluginPath();
if (existsSync(p) && !readFileSync(p, 'utf8').includes('caveman:native-opencode')) {
  // back up or move the file before running the installer
}

Try / catch

try {
  await cavemanEnableOpencode();
} catch (e) {
  if (String(e.message).includes('already exists and is not Caveman-owned')) {
    backupAndMove(pluginPath);
    await cavemanEnableOpencode();
  }
}

Prevention

When it happens

Trigger: Running the enable/install flow (`caveman` native opencode setup) when a file already exists at the plugin path and its contents do not contain the 'caveman:native-opencode' marker (e.g. a manually created plugin, or one from another tool).

Common situations: Users who already wrote a custom opencode plugin at the same location; leftover plugin files from a different tool or an older Caveman version whose marker changed; a partially edited Caveman plugin where the marker line was removed.

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


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

Appendix: source

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

  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 3ee70a1026)