{"record":{"id":"1c90ecb758ec9e83","repo":"JuliusBrussee/caveman","slug":"agent-native-bundle-removal-failed-error-as-er","errorCode":null,"errorMessage":"agent-native bundle removal failed: ${(error as Error).message}; rollback incomplete: ${(rollback as Error).message}","messagePattern":"agent-native bundle removal failed: (.+?); rollback incomplete: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"packages/cli/src/index.ts","lineNumber":2634,"sourceCode":"  if (journal.native_owned) {\n    const nativeJournal = readNativeJournal(agent);\n    if (nativeJournal) {\n      for (const operation of nativeJournal.operations) restoreNativeOperation(operation);\n    }\n  }\n  atomicWriteFile(agentNativeBundleRemovalJournalPath(agent), Buffer.from(JSON.stringify(journal, null, 2) + \"\\n\"));\n  try {\n    restoreAgentNativeBundleSkills(journal.skills);\n    restoreAgentNativeCloudMcp(agent, journal.previous_cloud_mcp);\n    if (journal.native_owned) disableNativeAgent(agent);\n    unlinkSync(agentNativeBundleJournalPath(agent));\n    unlinkSync(agentNativeBundleRemovalJournalPath(agent));\n  } catch (error) {\n    try {\n      restoreInstalledAgentNativeBundle(agent, journal);\n      unlinkSync(agentNativeBundleRemovalJournalPath(agent));\n    } catch (rollback) {\n      throw new Error(`agent-native bundle removal failed: ${(error as Error).message}; rollback incomplete: ${(rollback as Error).message}`);\n    }\n    throw new Error(`agent-native bundle removal failed: ${(error as Error).message}; installed bundle restored`);\n  }\n  process.stderr.write(`${mark(\"ok\")} ${agent}: agent-native bundle removed; prior skills and cloud MCP restored\\n`);\n}\n\nasync function setup(argv: string[] = []) {\n  const json = argv.includes(\"--json\");\n  const install = argv.includes(\"--install\");\n  const removeBundle = argv.includes(\"--remove\");\n  const agentNative = flagFrom(argv, \"--agent-native\", \"\");\n  const hasAgentNativeFlag = argv.includes(\"--agent-native\") || argv.some((arg) => arg.startsWith(\"--agent-native=\"));\n  const unknown = argv.filter((arg, index) =>\n    arg !== \"--json\"\n    && arg !== \"--install\"\n    && arg !== \"--remove\"\n    && arg !== \"--agent-native\"\n    && !arg.startsWith(\"--agent-native=\")","sourceCodeStart":2616,"sourceCodeEnd":2652,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/cli/src/index.ts#L2616-L2652","documentation":"Worst-case outcome of removeAgentNativeBundle: the removal sequence (restore skills, restore previous cloud MCP, disable native agent, unlink journals) threw, AND the compensating rollback (restoreInstalledAgentNativeBundle + unlink of the removal journal) also threw. The system is now in an unknown partially-removed state and the removal journal file remains on disk for recovery.","triggerScenarios":"Running --remove when filesystem operations fail mid-sequence: permission loss, skills directory removed externally, config path unwritable — and the restore-back attempt hits the same filesystem problem.","commonSituations":"Agent config file locked by a running agent (Windows/locked-file semantics); read-only home directory; disk full; skills directory deleted by hand while the journal still references it.","solutions":["Fix the underlying filesystem problem named in the rollback message (permissions, disk space, locked file)","Close the running agent, then re-run `caveman setup --agent-native <agent> --remove`; recoverPendingAgentNativeRemoval picks up the on-disk removal journal","If recovery loops, remove the bundle journal and agent-native entries manually after backing up the agent config"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// pre-flight for --remove: agent not running, config writable\nimport { accessSync, constants } from \"node:fs\";\naccessSync(agentConfigPath, constants.W_OK);\nif (await isProcessRunning(agentBinaryName)) throw new Error(`close ${agentBinaryName} before bundle removal`);","typeGuard":null,"tryCatchPattern":"try {\n  await setup([\"--agent-native\", agent, \"--remove\"]);\n} catch (error) {\n  if (/rollback incomplete/.test((error as Error).message)) {\n    // do NOT retry blindly; the removal journal on disk is the recovery point.\n    // fix the filesystem condition named in the message, then re-run --remove:\n    // recoverPendingAgentNativeRemoval completes or restarts the removal.\n    logAndPageOncall(error);   // state is partially removed; needs operator attention\n  } else throw error;\n}","preventionTips":["Always close the agent before --remove to avoid locked config files","Keep the removal journal path writable; it is the crash-recovery record","Monitor free disk space and home-directory permissions before destructive ops"],"tags":["removal","rollback-failure","recovery","filesystem"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}