JuliusBrussee/caveman · error · Error

local learn snapshot is unreadable at ${snapshotPath}

Error message

local learn snapshot is unreadable at ${snapshotPath}

What it means

syncLocalPracticeFindings cannot read the local learn snapshot file at ~/.caveman/reports/caveman-learn.json (it exists but is unreadable/invalid JSON). Only a missing file (ENOENT) is treated as 'no snapshot'; other read failures surface this error.

Source

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

  const subject = `synced ${out.spans}${local ? " local" : ""} spans`;
  if (out.headlineCompressionRefused) {
    return `${subject} · compression headline refused: cache writes ${out.cacheCreationInputTokens} > cache reads ${out.cachedInputTokens} (inferred; no savings headline)${destination}`;
  }
  return `${subject} · ${out.tokensSaved} estimated tokens saved (inferred; counter basis ${out.tokenCountBasis})${destination}`;
}

// syncLocalPracticeFindings moves only stable ids + non-negative token rates
// from the current learn snapshot. Titles, suggestions, evidence, file paths,
// and payload text never leave the machine. Empty snapshots are sent so stale
// rows for this user are removed server-side.
async function syncLocalPracticeFindings(cfg: Config): Promise<PracticeSyncOutcome> {
  const snapshotPath = join(cavemanHome(), "reports", "caveman-learn.json");
  let raw: Record<string, unknown>;
  try {
    raw = JSON.parse(readFileSync(snapshotPath, "utf8")) as Record<string, unknown>;
  } catch (error) {
    if ((error as NodeJS.ErrnoException).code === "ENOENT") return { kind: "no_snapshot" };
    throw new Error(`local learn snapshot is unreadable at ${snapshotPath}`);
  }

  if (!Array.isArray(raw.sinks)) {
    throw new Error(`local learn snapshot has no sinks array at ${snapshotPath}`);
  }
  const observed = typeof raw.generated_at === "string" ? new Date(raw.generated_at) : null;
  if (!observed || Number.isNaN(observed.valueOf())) {
    throw new Error(`local learn snapshot has no valid generated_at at ${snapshotPath}`);
  }
  const sessions = typeof raw.sessions_scanned === "number" && Number.isFinite(raw.sessions_scanned)
    ? Math.max(0, Math.trunc(raw.sessions_scanned))
    : 0;
  const counter = (value: unknown) =>
    typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;

  const findings = raw.sinks
    .filter((sink): sink is Record<string, unknown> =>
      !!sink && typeof sink === "object" && !Array.isArray(sink))

View on GitHub (pinned to 5184b3d11a)

Solutions

  1. Regenerate the snapshot by running `caveman learn`
  2. Check file permissions on the path; delete a corrupt snapshot and re-learn
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at packages/cli/src/index.ts:9213 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of JuliusBrussee/caveman@5184b3d11a (2026-08-18). Data as JSON: /api/errors/8b2818a3af567ae0. Report an issue: GitHub.