astrid-runtime/astrid · error

invalid Astrid volume record magic at

Error message

invalid Astrid volume record magic at {offset}

What it means

read_header reads the fixed-size record prefix and requires it to begin with RECORD_MAGIC. A mismatch means the bytes at the given offset are not the start of a valid volume record, so recovery/commit decoding cannot proceed and raises InvalidData with the offending offset included in the message.

Solutions

  1. Use the reported offset to inspect the file (`dd`/`xxd` around that offset) and confirm the corruption extent.
  2. Let recovery stop at the last valid commit — treat records after the bad offset as lost and rely on the last consistent snapshot.
  3. Verify writer/reader astrid-storage versions match.
  4. Restore the volume from backup if corruption precedes the last valid commit.
Defensive patterns

Strategy: try-catch

Try / catch

match recover_container(&mut file) {
    Err(e) if e.kind() == io::ErrorKind::InvalidData
        && e.to_string().starts_with("invalid Astrid volume record magic") => {
        eprintln!("record corruption: {e}; falling back to last valid commit");
        /* open read-only at last valid snapshot or restore from backup */
    }
    other => other,
}

Prevention

When it happens

Trigger: recover_from_headers or read_commit walking to an offset whose bytes don't start with RECORD_MAGIC — a corrupted record, a torn/partial write, following a stale next-record pointer, or reading a volume written by an incompatible version.

Common situations: Power loss / crash mid-write leaving a half-written record; the recovery pointer referencing a record that was later overwritten; bit rot on the storage device; version skew between the writer's record layout and the reader.

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


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

Appendix: source

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

                .ok_or_else(|| invalid_transition("missing metadata transaction payload"))?;
            let mutations = decode_metadata_mutations(payload)?;
            apply_metadata_mutations(regions, &mutations)?;
        },
    }
    Ok(())
}

fn read_header(file: &File, offset: u64, physical_len: u64) -> io::Result<Option<RecordHeader>> {
    let remaining = physical_len.saturating_sub(offset);
    if remaining < RECORD_FIXED_BYTES as u64 {
        return Ok(None);
    }
    #[cfg(test)]
    READ_HEADER_COUNT.with(|count| count.set(count.get().saturating_add(1)));
    let mut fixed = [0_u8; RECORD_FIXED_BYTES];
    read_exact_at(file, offset, &mut fixed)?;
    if fixed[..RECORD_MAGIC.len()] != RECORD_MAGIC {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("invalid Astrid volume record magic at {offset}"),
        ));
    }
    let total_len = u64::from_le_bytes(read_array::<8>(&fixed[8..16])?);
    let name_length = usize::from(u16::from_le_bytes(read_array::<2>(&fixed[25..27])?));
    let payload_len = u64::from_le_bytes(read_array::<8>(&fixed[35..43])?);
    let declared = (RECORD_FIXED_BYTES as u64)
        .checked_add(name_length as u64)
        .and_then(|value| value.checked_add(payload_len));
    if total_len < RECORD_FIXED_BYTES as u64
        || declared != Some(total_len)
        || name_length == 0
        || name_length > MAX_REGION_NAME_BYTES
    {
        return handle_bad_length(file, offset, physical_len, total_len, remaining);
    }
    if total_len > remaining {

View on GitHub (pinned to affd8760f4)