astrid-runtime/astrid · error

truncated Astrid volume record

Error message

truncated Astrid volume record

What it means

The positional read helper read_exact_at loops until it fills the caller's buffer from a fixed file offset. When the underlying read returns 0 bytes before the buffer is full (premature EOF), the requested record bytes do not exist in the file, so it raises UnexpectedEof 'truncated Astrid volume record'. All volume parsing (headers, payloads, footers) funnels through this helper, so any parse that runs past the file's end produces this error.

Solutions

  1. Check the file size against the expected volume size; if it's short, re-copy or restore the complete file.
  2. Restore the volume from backup or the last committed footer/commit point.
  3. Discard the torn tail record if the region tolerates losing the final write, then re-open the volume.
  4. Verify the recovery is reading the intended file path and not a stale or partial copy.
Defensive patterns

Strategy: validation

Validate before calling

// verify the file is large enough before attempting recovery
let len = file.metadata()?.len();
if footer_offset + FOOTER_BYTES > len {
    return Err("volume file is truncated; restore before recovering");
}

Try / catch

match recover_from_headers(&file) {
    Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
        // truncated: restore full file from backup or recover only committed prefix
    }
    other => other?,
}

Prevention

When it happens

Trigger: Any of read_header, read_record_payload, recover_footer_at, recover_from_headers, has_physically_valid_record_after, or read_header_without_tail_scan asks for N bytes at an offset where fewer than N bytes exist — a header length field points beyond the file end, the footer offset is past EOF, or the file was truncated mid-record.

Common situations: The volume file was copied incompletely or truncated (disk full, interrupted rsync/cp); a crashed writer left a partial final record; recovery is pointed at the wrong (shorter) file; a corrupt length field makes the parser read past the real end.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/53a9344c8a2af1fe. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-storage/src/volume/hosted/recover.rs:712

    *cursor = end;
    Ok(value)
}

fn read_array<const N: usize>(bytes: &[u8]) -> io::Result<[u8; N]> {
    bytes
        .try_into()
        .map_err(|_| invalid_transition("invalid volume record integer"))
}

fn read_exact_at(file: &File, offset: u64, buffer: &mut [u8]) -> io::Result<()> {
    let mut done = 0_usize;
    while done < buffer.len() {
        let read_offset = offset
            .checked_add(done as u64)
            .ok_or_else(|| io::Error::other("volume positional read offset overflow"))?;
        let read = file_read_at(file, &mut buffer[done..], read_offset)?;
        if read == 0 {
            return Err(io::Error::new(
                io::ErrorKind::UnexpectedEof,
                "truncated Astrid volume record",
            ));
        }
        done = done
            .checked_add(read)
            .ok_or_else(|| io::Error::other("volume positional read length overflow"))?;
    }
    Ok(())
}

#[cfg(unix)]
fn file_read_at(file: &File, buffer: &mut [u8], offset: u64) -> io::Result<usize> {
    use std::os::unix::fs::FileExt as _;
    file.read_at(buffer, offset)
}

#[cfg(windows)]

View on GitHub (pinned to affd8760f4)