JuliusBrussee/caveman · error

existing Codex Caveman block is corrupted; run `caveman…

Error message

existing Codex Caveman block is corrupted; run `caveman doctor codex`

What it means

The Codex installer strips previously-installed Caveman TOML blocks (root, tables, provider, MCP) by matching begin/end markers before re-emitting them. If exactly one of a begin/end marker pair is present, or the end marker appears before the begin marker, the block is considered corrupted and the installer refuses to proceed, directing the user to `caveman doctor codex`.

Solutions

  1. Run `caveman doctor codex` to detect and repair the corrupted block, then re-run enable.
  2. Manually remove all remaining Caveman marker lines and enclosed content from the TOML so the config is clean, then re-run install.
  3. Restore config.toml from a backup taken before the Caveman install and re-run the enable flow.

Example fix

// before (config.toml)
# caveman:native-root-begin
model_provider = "caveman"
// (end marker missing)
// after `caveman doctor codex`
model_provider = "openai"  // clean config; re-run enable to re-add complete block
Defensive patterns

Strategy: validation

Validate before calling

const toml = readFileSync(codexCfg, 'utf8');
for (const [b, e] of [[ROOT_BEGIN, ROOT_END], [TABLES_BEGIN, TABLES_END]]) {
  const s = toml.indexOf(b), f = toml.indexOf(e);
  if ((s === -1) !== (f === -1) || f < s) {
    // run `caveman doctor codex` before enabling
  }
}

Try / catch

try {
  await cavemanEnableCodex();
} catch (e) {
  if (String(e.message).includes('corrupted')) {
    await cavemanDoctorCodex();
    await cavemanEnableCodex();
  }
}

Prevention

When it happens

Trigger: Running the Codex enable/install flow when ~/.codex/config.toml contains a half-intact Caveman block: a begin marker without its end marker, an end marker without its begin, or end preceding begin.

Common situations: Manual edits to config.toml that deleted only part of a Caveman block; interrupted installs or rollbacks; comment-stripping/minifying tools removing marker comment lines; copy/paste edits that moved lines out of order.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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

Appendix: source

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

      kind: "aider-core",
      owned: { marker: AIDER_NATIVE_CORE_MARKER },
    },
  ];
}

function codexNativeConfig(source: string, gw: string, subscription: boolean, mcpBinary: string): { text: string; rootBlock: string; tablesBlock: string } {
  // Remove caveman's own marker blocks FIRST. The legacy table strippers below
  // skip every line after a caveman table until the next TOML header, and the
  // tables block ends with [mcp_servers.caveman] followed by the end marker, so
  // running them first ate "# <<< caveman:native-tables" and the block this
  // function had itself written failed its own re-parse as "corrupted" on the
  // next wrap (every `caveman codex` run fell back to session-only wrap and
  // doctor reported drift).
  let stripped = source;
  for (const [begin, end] of [[CODEX_NATIVE_ROOT_BEGIN, CODEX_NATIVE_ROOT_END], [CODEX_NATIVE_TABLES_BEGIN, CODEX_NATIVE_TABLES_END]] as const) {
    const start = stripped.indexOf(begin);
    const finish = stripped.indexOf(end);
    if ((start === -1) !== (finish === -1) || finish < start) throw new Error("existing Codex Caveman block is corrupted; run `caveman doctor codex`");
    if (start !== -1) stripped = `${stripped.slice(0, start)}${stripped.slice(finish + end.length)}`.trim();
  }
  stripped = stripCodexCavemanMcpToml(stripCodexCavemanProviderToml(stripped));
  const rootBlock = `${CODEX_NATIVE_ROOT_BEGIN}\nmodel_provider = "caveman"\n${CODEX_NATIVE_ROOT_END}`;
  const providerLines = codexCavemanProviderToml(gw, subscription).split("\n").slice(1).join("\n");
  const tablesBlock = [
    CODEX_NATIVE_TABLES_BEGIN,
    providerLines,
    "",
    "[mcp_servers.caveman]",
    `command = ${JSON.stringify(mcpBinary)}`,
    CODEX_RECOVERY_ENV,
    CODEX_NATIVE_TABLES_END,
  ].join("\n");
  const middle = stripped ? `\n\n${stripped}` : "";
  return { text: `${rootBlock}${middle}\n\n${tablesBlock}\n`, rootBlock, tablesBlock };
}

View on GitHub (pinned to 3ee70a1026)