astrid-runtime/astrid · error

region name too long

Error message

region name too long

What it means

When serializing a record, the region name is length-prefixed with a u16 on disk. append_from_inner converts the region name's byte length to u16 and fails with InvalidInput if the name exceeds 65535 bytes. This guards the on-disk record format, which cannot represent longer names.

Solutions

  1. Validate region names at your API boundary: reject names whose byte length exceeds 65535 (ideally far lower) before creating/writing the region.
  2. Truncate or hash long identifiers into a bounded name (e.g. blake3/sha256 hex, ≤64 chars) instead of using raw long strings.
  3. Count bytes, not characters, when validating names with non-ASCII content.
  4. Check where the name is assembled; cap the size of each concatenated component.

Example fix

// before
let name = format!("regions/{}:{}:{}", tenant, path, full_blob_id);
volume.write_region_from(&name, offset, payload)?;
// after
let digest = blake3::hash(full_blob_id.as_bytes()).to_hex()[..32].to_string();
let name = format!("r/{}:{}", tenant_short, digest);
assert!(name.len() <= 255);
volume.write_region_from(&name, offset, payload)?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust: validate name before calling the volume API
fn valid_region_name(name: &str) -> bool {
    !name.is_empty() && name.len() <= 255 // byte length, well under u16::MAX
}
if !valid_region_name(&region) { return Err("region name too long"); }

Try / catch

match volume.write_region_from(&region, offset, payload).await {
    Err(e) if e.kind() == io::ErrorKind::InvalidInput && e.to_string().contains("region name too long") => {
        // hash/shorten the identifier and retry with a bounded name
    }
    other => other?,
}

Prevention

When it happens

Trigger: append_from (via append_from_inner) is given a region whose as_str().as_bytes() length is > 65535 — callers constructing region names from unbounded user input, concatenations, or long identifiers.

Common situations: User-supplied or templated region names pasted into the volume API without validation; generated names built by concatenating prefixes/IDs that grow unbounded; names containing multi-byte UTF-8 whose byte length (not char count) exceeds the limit.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at crates/astrid-storage/src/volume/hosted/stream.rs:101

        },
    }
}

fn append_from_inner(
    state: &mut ContainerState,
    operation: Operation,
    region: &VolumeRegion,
    offset: u64,
    payload_len: u64,
    payload: &mut dyn Read,
) -> io::Result<(u64, u64)> {
    let next_sequence = state
        .sequence
        .checked_add(1)
        .ok_or_else(|| io::Error::other("Astrid volume sequence exhausted"))?;
    let name = region.as_str().as_bytes();
    let name_len = u16::try_from(name.len())
        .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "region name too long"))?;
    let total_len = u64::try_from(RECORD_FIXED_BYTES)
        .ok()
        .and_then(|fixed| fixed.checked_add(u64::from(name_len)))
        .and_then(|total| total.checked_add(payload_len))
        .ok_or_else(|| io::Error::other("volume record length overflow"))?;
    let mut hasher = blake3::Hasher::new_derive_key("astrid volume record v1");
    hasher.update(&next_sequence.to_le_bytes());
    hasher.update(&[operation as u8]);
    hasher.update(&name_len.to_le_bytes());
    hasher.update(&offset.to_le_bytes());
    hasher.update(&payload_len.to_le_bytes());
    hasher.update(name);

    // The previous footer ends at `valid_len`; remove it before publishing a
    // new record. A failed stream leaves footer_pending set so the next sync
    // restores the prior durable footer.
    state.flush_state = super::FlushState::Required;
    state.footer_pending = true;

View on GitHub (pinned to affd8760f4)