affaan-m/ECC · error · CapsuleError
${state.reason}
Error message
${state.reason} What it means
The public entries() method re-reads and verifies the journal via readJournal(); if the journal is missing, truncated, or fails hash verification it throws a CapsuleError with the read's code, message (state.reason), and failed_at offset. Callers get a precise, diagnostic failure instead of a partially verified entry list, since an unverified journal cannot be trusted.
Solutions
- Inspect the error's code and failed_at to locate the first bad byte/entry in the journal.
- Repair a torn final line by truncating the incomplete trailing line only if you accept losing that entry, then re-verify.
- Restore the journal from a backup or rebuild the capsule if earlier entries fail hash verification.
- Always use capsule.entries() (locked/verified) rather than reading the JSONL file directly.
Example fix
// before: reading the raw file and choking on a torn line
const lines = fs.readFileSync('capsule/journal.jsonl', 'utf8').trim().split('\n');
// after: use the verified accessor with explicit failure handling
try {
const entries = capsule.entries();
} catch (e) {
console.error(`journal unreadable at ${e.detail?.failed_at}: ${e.message}; restore from backup`);
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
function canReadJournal(capsule) {
const state = readJournal(capsule.journalPath);
return state.ok === true;
} Type guard
function isVerifiedEntryList(state) {
return state != null && state.ok === true && Array.isArray(state.entries);
} Try / catch
try {
const entries = capsule.entries();
} catch (e) {
if (e instanceof CapsuleError && e.detail?.failed_at != null) {
console.error(`journal corrupt from offset ${e.detail.failed_at}: ${e.message}`);
// fall back to last known-good snapshot or abort the report
}
throw e;
} Prevention
- Always read via capsule.entries(), never by parsing journal.jsonl directly.
- Verify capsules after any copy/transfer before consuming them.
- Avoid concurrent external writers to the same capsule directory.
- Snapshot journals (with hash verification) at run completion for later reads.
When it happens
Trigger: Calling capsule.entries() when the journal file does not exist, ends mid-line (torn write), contains hand-edited lines, or its stored digest/parent-hash chain no longer verifies.
Common situations: Reading entries after a process was killed mid-append (partial last line); inspecting a capsule copied with rsync --partial; someone concatenated or trimmed journal files; concurrent readers observing a file during an external (non-locked) modification.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- state.reason
- all overlays must be readable local files
- all takes must be readable local files
- artifact byte count exceeded during reading
- artifact byte count mismatch
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/e39c1d157262d54c.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/eval-harness/capsule.js:238
offset += written;
}
fs.fsyncSync(fd);
} finally {
fs.closeSync(fd);
}
// These fields remain observable for compatibility, but are never used as
// authoritative append state. A preopened handle always reloads above.
this.meta = state.meta;
this.lastHash = entry.entry_hash;
this.nextSeq = entry.seq + 1;
return entry;
});
}
entries() {
const state = readJournal(this.journalPath);
if (!state.ok) {
throw new CapsuleError(state.code, state.reason, { failed_at: state.failed_at });
}
return state.entries;
}
}
/**
* Read and verify a journal file. Never throws for content problems; the
* result names the first failing entry index and a stable reason code.
*/
function readJournal(journalPath) {
if (!fs.existsSync(journalPath)) {
return { ok: false, code: 'capsule.missing_journal', reason: 'journal file missing', failed_at: null, entries: [] };
}
let bytes;
try { bytes = fs.readFileSync(journalPath); } catch {
return { ok: false, code: 'capsule.unreadable_journal', reason: 'journal file could not be read', failed_at: null, entries: [] };
}
const raw = bytes.toString('utf8');View on GitHub (pinned to 8321021c54)