JuliusBrussee/caveman · error
was removed after enable; refusing destructive disable
Error message
${operation.file} was removed after enable; refusing destructive disable What it means
While restoring an enable operation, the CLI compares the current bytes of the operation's target file with what existed before. If the file is now missing (`!current`) but the journal records that it existed before enable (`before_exists`), something deleted it after enable. Restoring would mean recreating a file whose prior content the backup may not fully capture, so the CLI refuses rather than perform a destructive operation.
Solutions
- Restore the deleted file from an external backup (git, editor history, OS backup) so the journal's before-state is meaningful, then retry disable.
- If deletion is intentional, delete the journal operation entry for this file so disable skips it.
- Recreate the file with the pre-enable content stored in the operation backup (`nativeBackupBytes`), then re-run disable.
- Run `caveman doctor <agent> --fix` to rebuild the config from the journal, then disable.
Example fix
// before: file missing, disable refuses $ caveman disable gemini // after: restore the file first, then disable cp ~/.config/gemini/settings.json.bak ~/.config/gemini/settings.json caveman disable gemini
Defensive patterns
Strategy: validation
Validate before calling
import { existsSync } from "node:fs";
for (const op of journal.operations) {
if (op.before_exists && !existsSync(op.file)) {
console.error(`refusing disable: ${op.file} was recorded pre-enable but is now missing`);
}
} Prevention
- Don't delete agent settings files while a caveman integration is enabled; disable first.
- Back up config directories before running reset/cleanup scripts.
- Keep agent config files under version control so accidental deletes are recoverable.
When it happens
Trigger: `restoreNativeOperation(operation)` where `fileBytes(operation.file)` returns null/empty while `operation.before_exists === true`.
Common situations: The user (or another tool) deleted the agent's settings/config file after running `caveman enable`; a reset script wiped the config directory; the file path moved between versions.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- Caveman hook changed after enable; refusing destructive…
- Claude ANTHROPIC_BASE_URL changed after enable; refusing…
- Claude caveman MCP entry changed after enable; refusing…
- Gemini caveman MCP entry changed after enable; refusing…
- Gemini integration journal lacks owned routing block
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/b8d4c363b3bd2d2f.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/index.ts:8254
if (identity === `native-hook:${agent}`) return false;
return !expectedStrings.has(canonicalManagedHookEntry(entry));
});
if (kept.length > 0) hooks[event] = kept;
else delete hooks[event];
}
if (Object.keys(hooks).length === 0) delete root.hooks;
return root;
}
function jsonBytes(root: Record<string, unknown>): Buffer {
return Buffer.from(JSON.stringify(root, null, 2) + "\n");
}
function restoreNativeOperation(operation: NativeJournal["operations"][number]): Buffer | null {
const current = fileBytes(operation.file);
const before = nativeBackupBytes(operation);
if (!current) {
if (operation.before_exists) throw new Error(`${operation.file} was removed after enable; refusing destructive disable`);
return null;
}
if (bytesHash(current) === operation.after_sha256) return before;
if (operation.kind === "claude-settings") {
const currentRoot = parseJsonFileObject(operation.file, current);
const beforeRoot = parseJsonFileObject(operation.file, before);
const currentEnv = currentRoot.env && typeof currentRoot.env === "object" && !Array.isArray(currentRoot.env)
? currentRoot.env as Record<string, unknown>
: {};
const beforeEnv = beforeRoot.env && typeof beforeRoot.env === "object" && !Array.isArray(beforeRoot.env)
? beforeRoot.env as Record<string, unknown>
: {};
const route = operation.owned?.route;
if (currentEnv.ANTHROPIC_BASE_URL !== undefined && currentEnv.ANTHROPIC_BASE_URL !== route) {
throw new Error("Claude ANTHROPIC_BASE_URL changed after enable; refusing destructive disable");
}
if (currentEnv.ANTHROPIC_BASE_URL === route) {View on GitHub (pinned to 3ee70a1026)