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

  1. Restore the deleted file from an external backup (git, editor history, OS backup) so the journal's before-state is meaningful, then retry disable.
  2. If deletion is intentional, delete the journal operation entry for this file so disable skips it.
  3. Recreate the file with the pre-enable content stored in the operation backup (`nativeBackupBytes`), then re-run disable.
  4. 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

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


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)