JuliusBrussee/caveman · error

Aider integration journal lacks owned blocks

Error message

Aider integration journal lacks owned blocks

What it means

Aider disable requires the enable journal (operation.owned) to contain the exact route_block and read_block strings that were injected into the Aider config. If either is missing (not a string), the rollback cannot be performed safely, so the tool throws. This is a journal-corruption / missing-state error, not a config error.

Solutions

  1. Delete/rename the stale journal state and manually remove the Caveman blocks from .aider.conf.yml.
  2. Re-run enable to regenerate a complete journal, then disable.
  3. Upgrade the CLI so journal reader and writer use the same schema.

Example fix

// journal state missing blocks (corrupt)
{ "kind": "aider-config", "owned": { "route_block": null } }
// after: re-enable to regenerate
{ "kind": "aider-config", "owned": { "route_block": "# caveman-start ...", "read_block": "# caveman-read ..." } }
Defensive patterns

Strategy: validation

Validate before calling

const owned = journal.owned ?? {};
if (typeof owned.route_block !== 'string' || typeof owned.read_block !== 'string') {
  throw new Error('Aider journal incomplete; re-enable before disabling');
}

Type guard

const hasAiderBlocks = (owned) =>
  typeof owned?.route_block === 'string' && owned.route_block.length > 0 &&
  typeof owned?.read_block === 'string' && owned.read_block.length > 0;

Try / catch

try {
  await caveman.disable(['aider']);
} catch (err) {
  if (err.message.includes('journal lacks owned blocks')) {
    console.error('Journal corrupt/incomplete; remove blocks from .aider.conf.yml manually.');
  } else throw err;
}

Prevention

When it happens

Trigger: Running the disable operation with operation.kind === "aider-config" while operation.owned.route_block or operation.owned.read_block is undefined or not a string — e.g. journal written by an older version, partially written, or hand-deleted.

Common situations: Version upgrade changed the journal schema; state file truncated or edited; disabling on a machine where the state was synced incompletely.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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

Appendix: source

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

    const mcp = root.mcp && typeof root.mcp === "object" && !Array.isArray(root.mcp) ? root.mcp as Record<string, unknown> : {};
    if (mcp.caveman !== undefined && JSON.stringify(mcp.caveman) !== JSON.stringify(operation.owned?.installed_mcp)) {
      throw new Error("OpenCode caveman MCP entry changed after enable; refusing destructive disable");
    }
    if (mcp.caveman !== undefined) {
      if (operation.owned?.previous_mcp === null || operation.owned?.previous_mcp === undefined) delete mcp.caveman;
      else mcp.caveman = operation.owned.previous_mcp;
    }
    if (Object.keys(mcp).length > 0) root.mcp = mcp;
    else delete root.mcp;
    return jsonBytes(root);
  }
  if (operation.kind === "aider-config") {
    let text = current.toString("utf8");
    const routeBlock = operation.owned?.route_block;
    const readBlock = operation.owned?.read_block;
    const previousRouteLine = operation.owned?.previous_route_line;
    if (typeof routeBlock !== "string" || typeof readBlock !== "string") {
      throw new Error("Aider integration journal lacks owned blocks");
    }
    if (!text.includes(routeBlock) || !text.includes(readBlock)) {
      throw new Error("Aider Caveman config block changed after enable; refusing destructive disable");
    }
    text = text.replace(routeBlock, typeof previousRouteLine === "string" ? previousRouteLine : "");
    text = text.replace(readBlock, "");
    return Buffer.from(text.replace(/\n{3,}/g, "\n\n"));
  }
  if (operation.kind === "aider-core") {
    throw new Error(`${operation.file} changed after enable; refusing destructive disable`);
  }
  if (operation.kind === "hermes-plugin-manifest" || operation.kind === "hermes-plugin-init") {
    throw new Error(`${operation.file} changed after enable; refusing destructive disable`);
  }
  if (operation.kind === "hermes-config") {
    let text = current.toString("utf8");
    const routeBlock = operation.owned?.route_block;
    const previousRouteLines = operation.owned?.previous_route_lines;

View on GitHub (pinned to 3ee70a1026)