Hmbown/CodeWhale · error · io::Error
late usage ledger exceeds its size bound
Error message
late usage ledger exceeds its size bound
What it means
write_late_usage_ledger enforces a 1 MiB cap (MAX_LATE_USAGE_LEDGER_BYTES) on the serialized ledger JSON. If serde_json::to_vec produces more bytes than the cap, the write is refused with this InvalidData io::Error instead of writing an unbounded file. This keeps the ledger a bounded, replayable file rather than letting it grow without limit. Thrown from crates/tui/src/session_manager.rs:1476.
Solutions
- Prune old/finished entries from the LateUsageLedger before writing so the JSON stays under 1 MiB.
- Check the ledger file size on disk (ls -l) and delete/reset the ledger file if its contents are disposable; Codewhale will start from a default ledger.
- Compact representation: shorten stored data (store IDs instead of full paths) if entries are inherently large.
- If 1 MiB is genuinely too small for legitimate data, raise MAX_LATE_USAGE_LEDGER_BYTES deliberately and document the change — do not remove the bound.
Example fix
// before: write everything, unbounded growth ledger.entries.insert(key, entry); write_late_usage_ledger(path, &ledger)?; // after: prune stale entries first ledger.entries.retain(|_, e| e.last_seen >= cutoff); write_late_usage_ledger(path, &ledger)?;
Defensive patterns
Strategy: validation
Validate before calling
let bytes = serde_json::to_vec(&ledger).map_err(io::Error::other)?;
if bytes.len() > 1024 * 1024 {
ledger.entries.retain(|_, e| e.last_seen >= cutoff); // prune, then retry
} Type guard
fn ledger_fits(ledger: &LateUsageLedger) -> bool {
serde_json::to_vec(ledger).map_or(false, |b| b.len() <= 1024 * 1024)
} Try / catch
if let Err(e) = write_late_usage_ledger(path, &ledger) {
if e.to_string().contains("exceeds its size bound") {
let mut pruned = ledger.clone();
pruned.entries.retain(|_, v| v.last_seen >= cutoff);
write_late_usage_ledger(path, &pruned)?;
} else { return Err(e); }
} Prevention
- Prune stale entries on every persist instead of letting the ledger grow monotonically.
- Store compact identifiers (IDs) rather than full paths in ledger entries.
- Check the ledger file size periodically (ls -l) in long-running installs.
- Never disable the size bound; raise the constant deliberately if data legitimately grows.
When it happens
Trigger: Persisting a LateUsageLedger whose accumulated entries exceed 1024*1024 bytes of JSON — many tracked sessions with long paths/IDs, or entries never pruned after long use, so to_vec exceeds MAX_LATE_USAGE_LEDGER_BYTES before write_atomic is called.
Common situations: Long-running installs where the ledger accumulates thousands of late-usage entries; sessions with very long file paths inflating per-entry size; an earlier version or fork wrote a larger ledger and newer code now refuses to rewrite it; tests that build a huge synthetic ledger.
Understand the failure class
Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.
Related errors
- late usage ledger exceeds its size bound
- invalid late usage deletion marker
- <serde_json deserialization error>
- session accounting was deleted
- session was deleted
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/f0d9ac331eff5aa4.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/session_manager.rs:1476
};
let mut marker = Vec::with_capacity(LATE_USAGE_DELETED.len());
tombstone
.take(u64::try_from(LATE_USAGE_DELETED.len()).unwrap_or(u64::MAX) + 1)
.read_to_end(&mut marker)?;
if marker != LATE_USAGE_DELETED {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid late usage deletion marker",
));
}
Ok(true)
}
fn write_late_usage_ledger(path: &Path, ledger: &LateUsageLedger) -> io::Result<()> {
let bytes = serde_json::to_vec(ledger)
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
if u64::try_from(bytes.len()).unwrap_or(u64::MAX) > MAX_LATE_USAGE_LEDGER_BYTES {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"late usage ledger exceeds its size bound",
));
}
write_atomic(path, &bytes)
}
fn load_late_usage_unlocked(path: &Path) -> io::Result<LateUsageLedger> {
let file = match open_private_read_file(path) {
Ok(file) => file,
Err(error) if error.kind() == io::ErrorKind::NotFound => {
return Ok(LateUsageLedger::default());
}
Err(error) => return Err(error),
};
let metadata = file.metadata()?;
if metadata.len() > MAX_LATE_USAGE_LEDGER_BYTES {
return Err(io::Error::new(View on GitHub (pinned to 73e0f67d83)