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

  1. Prune old/finished entries from the LateUsageLedger before writing so the JSON stays under 1 MiB.
  2. 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.
  3. Compact representation: shorten stored data (store IDs instead of full paths) if entries are inherently large.
  4. 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

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


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)