Hmbown/CodeWhale · error · io::Error
err
Error message
err
What it means
load_unlocked parses all receipt lines and then rebuilds session state via ApprovalReplay::from_receipts. If the receipts are individually valid JSON but their sequence is semantically inconsistent for replay (e.g. a state machine transition that cannot happen), the replay error is wrapped in an io::Error with InvalidData. This is a semantic-validation failure of the approval history, not a parse failure.
Solutions
- Inspect the log's receipt order and remove/reorder the offending line identified by replay's error text
- Delete the log to reset replay state if the history is not needed
- Verify all writers go through ApprovalLog::append (which takes the fd_lock write lock) rather than appending directly
- Check for version skew: replay rules may have changed between crate versions
Defensive patterns
Strategy: validation
Validate before calling
let receipts: Vec<ApprovalReceipt> = log.load(session_id)?; ApprovalReplay::from_receipts(&receipts)?; // surface semantic issues before use
Try / catch
let replay = log.replay(session_id)
.map_err(|e| e.context("approval history failed replay validation"))?; Prevention
- Only append receipts through ApprovalLog::append (it holds the lock and pre-validates)
- Keep receipt generation and replay logic versioned together
- Never merge logs across sessions
When it happens
Trigger: load/append call load_unlocked and the parsed receipt sequence violates ApprovalReplay's invariants — out-of-order receipts, a decision referencing an unknown request, or duplicate/conflicting receipts.
Common situations: Log assembled from merged sessions or copied between machines; concurrent writers bypassing the lock file; receipts from a version with different replay rules.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- A pinned task provider requires an explicit model
- agent profile provider cannot be empty
- agent profile provider must be a simple provider id
- Antigravity cloud-code request has no text contents
- Edit refused: it would leave
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/75c9fc12db205ae5.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/approval_log.rs:239
let path = self.log_path(session_id)?;
let file = match File::open(path) {
Ok(file) => file,
Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(Vec::new()),
Err(err) => return Err(err),
};
let mut receipts = Vec::new();
for (index, line) in BufReader::new(file).lines().enumerate() {
let line = line?;
let receipt = serde_json::from_str(&line).map_err(|err| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("invalid approval receipt at line {}: {err}", index + 1),
)
})?;
receipts.push(receipt);
}
ApprovalReplay::from_receipts(&receipts)
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
Ok(receipts)
}
pub(crate) fn load(&self, session_id: &str) -> io::Result<Vec<ApprovalReceipt>> {
if !self.log_path(session_id)?.exists() {
return Ok(Vec::new());
}
let Some(lock_file) = self.open_existing_lock_file(session_id)? else {
// Imported or legacy snapshots can contain a receipt log without
// its ephemeral lock file. Preserve read-only session loading;
// live writers always publish the lock before creating the log.
return self.load_unlocked(session_id);
};
let lock = fd_lock::RwLock::new(lock_file);
let _guard = lock.read()?;
self.load_unlocked(session_id)
}
View on GitHub (pinned to 433685b202)