Hmbown/CodeWhale · error · std::io::Error

InvalidData

InvalidData

Error message

invalid approval receipt at line {}: {err}

What it means

ApprovalLog::load_unlocked reads <sessions>/<session_id>/approval_receipts.jsonl line by line, parsing each line as a JSON ApprovalReceipt. Any line serde_json cannot decode produces InvalidData with the 1-based line number and the underlying serde error. A missing log file is Ok(empty); a present but malformed one is fatal for loading/replaying that session's approvals. After parsing, ApprovalReplay::from_receipts can fail similarly if the receipts are mutually inconsistent.

Source

Thrown at crates/tui/src/approval_log.rs:226

        match OpenOptions::new().read(true).open(path) {
            Ok(file) => Ok(Some(file)),
            Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
            Err(err) => Err(err),
        }
    }

    fn load_unlocked(&self, session_id: &str) -> io::Result<Vec<ApprovalReceipt>> {
        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;

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Open <home>/.codewhale/sessions/<session_id>/approval_receipts.jsonl, jump to the reported line (cat -n or jq), and fix or delete the malformed line
  2. If the session is disposable, delete that session's directory so an empty log is recreated and approvals re-prompt
  3. If the file was never touched, capture the full serde error and report a bug: the writer must only emit parseable receipts
  4. After a version change, start a fresh session or resume so the current schema rewrites the log

Example fix

# before
# line 17 of approval_receipts.jsonl is truncated garbage from a crash

# after: drop the corrupt line and re-verify
sed -i '17d' ~/.codewhale/sessions/<session_id>/approval_receipts.jsonl
codewhale sessions resume <session_id>
Defensive patterns

Strategy: try-catch

Try / catch

match approval_log.load(sid) {
    Err(e) if e.kind() == io::ErrorKind::InvalidData => {
        // e message names the 1-based line: quarantine the file, reset the session,
        // never silently drop approvals
        log::warn!("corrupt approval log: {e}; session {sid} will re-prompt");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Loading a session whose approval_receipts.jsonl contains a hand-edited line, a truncated final line (crash or disk-full during append), a blank line, non-JSON text, or receipts written by an older/newer schema version that serde_json::from_str rejects.

Common situations: Upgrading/downgrading the binary across a receipt-format change; users editing or merging log files by hand; a killed process leaving a partial last line; syncing the sessions dir through a tool that mangled encoding.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/e9b491ed8e2a66a8. Report an issue: GitHub.