astrid-runtime/astrid · error

non-UTF-8 region name

Error message

non-UTF-8 region name

What it means

While recovering a hosted volume's region records, the header's variable-length region name bytes are read from disk and validated as UTF-8. The on-disk region name is not valid UTF-8, so the name cannot be turned into a Rust String and mapped to a VolumeRegion. The library treats any corruption or foreign data in the name field as InvalidData rather than panicking.

Solutions

  1. Verify the file being recovered is actually an Astrid hosted volume produced by this format version; run recovery against the correct file.
  2. Restore the volume file from backup or snapshot if the name region is corrupted.
  3. Re-run recovery to skip past the corrupt record if the recovery path supports resync, or re-create the volume region and re-write its data.
  4. Check storage medium/filesystem health (fsck) if multiple regions fail with the same error.
Defensive patterns

Strategy: try-catch

Validate before calling

// before recovery: sanity-check the file looks like a volume
let magic = read_prefix_at(&file, 0)?;
if magic != EXPECTED_VOLUME_MAGIC { return Err("not an Astrid volume file"); }

Try / catch

match recover_from_headers(&file) {
    Err(e) if e.kind() == io::ErrorKind::InvalidData && e.to_string().contains("non-UTF-8 region name") => {
        // corrupt record: restore from backup or skip/resync recovery
    }
    other => other?,
}

Prevention

When it happens

Trigger: read_header (called by recover_from_headers and read_commit) parsed a record header whose name_length bytes, read at position_from(header start + RECORD_FIXED_BYTES), decode as invalid UTF-8 — e.g. a corrupted/torn write, a stale or zeroed name region, or a misaligned offset into the record stream.

Common situations: A volume file was partially written during a crash (name bytes torn mid-write); the file was corrupted by another tool or truncated/zeroed regions; a caller points recovery at the wrong file or at a file from a different/incompatible volume format, so the header length fields are interpreted incorrectly.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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

Appendix: source

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

        .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 {
        return handle_bad_length(file, offset, physical_len, total_len, remaining);
    }
    let operation = Operation::decode(fixed[24])?;
    let name_offset = offset
        .checked_add(RECORD_FIXED_BYTES as u64)
        .ok_or_else(|| io::Error::other("volume region name offset overflow"))?;
    let mut name = vec![0_u8; name_length];
    read_exact_at(file, name_offset, &mut name)?;
    let name = String::from_utf8(name)
        .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "non-UTF-8 region name"))?;
    let name = VolumeRegion::new(name)?;
    let payload_offset = name_offset
        .checked_add(name_length as u64)
        .ok_or_else(|| io::Error::other("volume payload offset overflow"))?;
    Ok(Some(RecordHeader {
        total_len,
        sequence: u64::from_le_bytes(read_array::<8>(&fixed[16..24])?),
        operation,
        logical_offset: u64::from_le_bytes(read_array::<8>(&fixed[27..35])?),
        checksum: read_array::<32>(&fixed[43..75])?,
        name,
        payload_offset,
        payload_len,
    }))
}

fn handle_bad_length(
    file: &File,

View on GitHub (pinned to affd8760f4)