JuliusBrussee/caveman · error

cannot read agent-native bundle journal

Error message

cannot read ${agent} agent-native bundle journal: ${(error as Error).message}

What it means

Reading the agent-native bundle journal (a JSON file recording which cloud MCP/skills were installed for 'claude' or 'codex') failed with an error other than ENOENT (missing file, which returns null). The installer wraps the underlying error message and rethrows so the user knows journal state is unreadable.

Solutions

  1. Delete the corrupt journal file so the CLI can recreate it (it treats absence as null).
  2. Check file permissions/ownership on the journal path (fix with chmod/chown).
  3. Inspect the file to confirm valid JSON with schema_version 1 and correct 'agent' field.
  4. Reinstall/upgrade the CLI so journal schema matches the current version.

Example fix

// before
cat ~/.caveman/claude-bundle.json  // corrupt JSON
// after
rm ~/.caveman/claude-bundle.json && caveman install  // recreates journal
Defensive patterns

Strategy: try-catch

Validate before calling

const p = journalPath(agent);
try { JSON.parse(readFileSync(p, 'utf8')); } catch { console.warn(`journal ${p} corrupt — delete it before installing`); }

Type guard

function isJournal(v: unknown): v is AgentNativeBundleJournal {
  return typeof v === 'object' && v !== null && (v as any).schema_version === 1 &&
    typeof (v as any).agent === 'string' && Array.isArray((v as any).skills);
}

Try / catch

try { await cavemanInstall() } catch (e) { if (String(e.message).includes('cannot read') && String(e.message).includes('journal')) { fs.rmSync(journalPath, { force: true }); await cavemanInstall(); } }

Prevention

When it happens

Trigger: readAgentNativeBundleJournal(agent) catches a non-ENOENT error from readFileSync/JSON.parse/schema validation of the journal file: EACCES/EISDIR on the journal path, invalid JSON, or schema_version !== 1 / wrong agent field / missing cloud_mcp or skills array.

Common situations: Journal file corrupted by a crash mid-write; file permissions changed after a user/UID switch; journal written by an older CLI with a different schema_version; journal path is a directory.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


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

Appendix: source

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

function agentNativeBundleJournalPath(agent: "claude" | "codex", pending = false): string {
  return join(cavemanHome(), "integrations", `${agent}.agent-native-bundle${pending ? ".pending" : ""}.json`);
}

function agentNativeBundleRemovalJournalPath(agent: "claude" | "codex"): string {
  return join(cavemanHome(), "integrations", `${agent}.agent-native-bundle.removing.json`);
}

function readAgentNativeBundleJournal(agent: "claude" | "codex"): AgentNativeBundleJournal | null {
  try {
    const value = JSON.parse(readFileSync(agentNativeBundleJournalPath(agent), "utf8")) as AgentNativeBundleJournal;
    if (value.schema_version !== 1 || value.agent !== agent || !value.cloud_mcp || !Array.isArray(value.skills)) {
      throw new Error("unsupported bundle journal");
    }
    return value;
  } catch (error) {
    if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
    throw new Error(`cannot read ${agent} agent-native bundle journal: ${(error as Error).message}`);
  }
}

function readPendingAgentNativeBundleJournal(agent: "claude" | "codex"): AgentNativeBundleJournal | null {
  try {
    const value = JSON.parse(readFileSync(agentNativeBundleJournalPath(agent, true), "utf8")) as AgentNativeBundleJournal;
    if (value.schema_version !== 1 || value.agent !== agent || !value.cloud_mcp || !Array.isArray(value.skills)) {
      throw new Error("unsupported pending bundle journal");
    }
    return value;
  } catch (error) {
    if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
    throw new Error(`cannot read pending ${agent} agent-native bundle journal: ${(error as Error).message}`);
  }
}

function readPendingAgentNativeBundleRemoval(agent: "claude" | "codex"): AgentNativeBundleJournal | null {
  try {

View on GitHub (pinned to 3ee70a1026)