Hmbown/CodeWhale · error · io::Error
invalid late usage deletion marker
Error message
invalid late usage deletion marker
What it means
This io::Error with ErrorKind::InvalidData is raised when reading the tombstone file that marks a session as late-usage-deleted: the file's contents must exactly equal the marker bytes `codewhale-session-deleted-v1\n` (LATE_USAGE_DELETED). If the file holds anything else, the library refuses to trust the tombstone because a corrupted or hand-edited file could otherwise be mistaken for a valid deletion record. It surfaces from the session manager's deletion-marker validation path in crates/tui/src/session_manager.rs:1464.
Solutions
- Delete the corrupted tombstone file so the session is no longer marked deleted, then let Codewhale recreate state normally.
- Restore the file to the exact marker bytes `codewhale-session-deleted-v1\n` if it was accidentally modified (printf 'codewhale-session-deleted-v1\n' > tombstone).
- Check for version mismatch: if Codewhale was upgraded/downgraded, the marker constant may have changed; re-sync session files from the matching version or clear the stale tombstones.
- Verify disk health/free space — a partial write during the original atomic write is a common corruption source; re-run the affected session operation.
Example fix
// before: tombstone file edited externally, contains "codewhale-session-deleted-v1\n\n" // after: restore exact marker bytes $ printf 'codewhale-session-deleted-v1\n' > ~/.local/state/codewhale/sessions/<id>.tombstone
Defensive patterns
Strategy: validation
Validate before calling
let marker = std::fs::read(tombstone_path)?;
if marker != b"codewhale-session-deleted-v1\n" {
// treat as corrupt: remove or restore the file before the library reads it
let _ = std::fs::remove_file(tombstone_path);
} Type guard
fn is_valid_tombstone(bytes: &[u8]) -> bool {
bytes == b"codewhale-session-deleted-v1\n"
} Try / catch
match manager.check_late_usage_tombstone(path) {
Ok(deleted) => { /* proceed */ }
Err(e) if e.kind() == io::ErrorKind::InvalidData => {
// corrupt marker: drop the tombstone and re-run
let _ = std::fs::remove_file(path);
}
Err(e) => return Err(e),
} Prevention
- Never open or save tombstone files with text editors; they may add BOMs or trailing bytes.
- Copy session state with byte-exact tools (cp, rsync), not converters that alter line endings.
- Keep all Codewhale instances on the same version so marker constants agree.
- Monitor disk-full/crash conditions; verify atomic writes completed after an unclean shutdown.
When it happens
Trigger: Calling the session manager's late-usage tombstone check when the tombstone file exists but its bytes differ from LATE_USAGE_DELETED — e.g. a truncated read, a partial write, the marker version string changed between Codewhale versions, or the file was edited/overwritten externally (read path does `file.take(len+1).read_to_end` then compares to the constant).
Common situations: Upgrading/downgrading Codewhale so the marker version suffix (`-v1`) no longer matches; a crash or disk-full during a previous atomic write leaving partial content; manually inspecting or touching files under the sessions directory with an editor that adds a trailing newline, BOM, or rewrites bytes; copying session state between machines via a tool that mangles the marker.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- late usage ledger exceeds its size bound
- late usage ledger exceeds its size bound
- <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/5657b716d8cd3e06.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/session_manager.rs:1464
}
Ok(paths)
}
/// A deletion marker and its stable lock survive deletion, without any
/// route or usage data. A captured callback must never recreate the ledger.
fn late_usage_is_deleted(path: &Path) -> io::Result<bool> {
use std::io::Read as _;
let tombstone = match open_private_read_file(&path.with_extension("deleted")) {
Ok(file) => file,
Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(false),
Err(error) => return Err(error),
};
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)
}View on GitHub (pinned to 73e0f67d83)