{"record":{"id":"e3bf476ad383c014","repo":"JuliusBrussee/caveman","slug":"extensionpath-already-exists-and-is-not-caveman-owned","errorCode":null,"errorMessage":"${extensionPath} already exists and is not Caveman-owned; refusing to overwrite it","messagePattern":"(.+?) already exists and is not Caveman-owned; refusing to overwrite it","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":7380,"sourceCode":"      owned: { routes, previous_routes: previousRoutes, installed_mcp: installedMcp, previous_mcp: previousMcp ?? null },\n    },\n    {\n      file: pluginPath,\n      before: pluginBefore,\n      after: Buffer.from(opencodeNativePluginSource()),\n      kind: \"opencode-plugin\",\n    },\n  ];\n}\n\nfunction piNativeMutations(): NativeMutation[] {\n  const extensionDir = join(homedir(), \".pi\", \"agent\", \"extensions\");\n  // Pi's extension auto-discovery accepts only .ts/.js (isExtensionFile in\n  // pi-coding-agent 0.84.2); a .mjs here would journal fine and never load.\n  const extensionPath = join(extensionDir, \"caveman-native.js\");\n  const before = fileBytes(extensionPath);\n  if (before && !before.toString(\"utf8\").includes(\"caveman:native-pi\")) {\n    throw new Error(`${extensionPath} already exists and is not Caveman-owned; refusing to overwrite it`);\n  }\n  // Plain `pi` launches carry no wrap env, so the persistent artifact bakes in\n  // the invocation this enable resolved (falling back to PATH when unset) —\n  // same pattern as the generated opencode plugin.\n  const { cmd, pre } = cavemanInvocation();\n  const after = Buffer.concat([\n    Buffer.from(\"// caveman:native-pi — GENERATED by `caveman enable pi`.\\n\"),\n    Buffer.from(`process.env.CAVEMAN_PI_HOOK_CMD ??= ${JSON.stringify(JSON.stringify([cmd, ...pre]))};\\n`),\n    readFileSync(resolvePiExtension()),\n  ]);\n  return [{\n    file: extensionPath,\n    before,\n    after,\n    kind: \"pi-extension\",\n    owned: { sha256: bytesHash(after), created_extension_dir: !existsSync(extensionDir) },\n  }];\n}","sourceCodeStart":7362,"sourceCodeEnd":7398,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L7362-L7398","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n~/.pi/agent/extensions/caveman-native.js  // no 'caveman:native-pi' marker\n// after\nmv ~/.pi/agent/extensions/caveman-native.js ~/.pi/agent/extensions/caveman-native.js.bak\n// re-run: caveman enable  -> regenerates file with '// caveman:native-pi'","handlingStrategy":"fallback","validationCode":"import { readFileSync, existsSync } from 'fs';\nconst p = join(homedir(), '.pi', 'agent', 'extensions', 'caveman-native.js');\nif (existsSync(p) && !readFileSync(p, 'utf8').includes('caveman:native-pi')) {\n  // rename or remove before enabling\n}","typeGuard":null,"tryCatchPattern":"try {\n  await cavemanEnablePi();\n} catch (e) {\n  if (String(e.message).includes('already exists and is not Caveman-owned')) {\n    rename(extensionPath, extensionPath + '.bak');\n    await cavemanEnablePi();\n  }\n}","preventionTips":["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."],"tags":["install","config","refuse-overwrite","cli"],"backgroundTag":"file-already-exists","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}