rustfs/rustfs · error · io::Error

Invalid erasure coding parameters

Error message

Invalid erasure coding parameters

What it means

Guard in Erasure::decode against on-disk metadata whose block_size or data_shards is zero — values that would otherwise cause a divide-by-zero panic on every GET. These fields come from xl.meta, so a zero means the FileInfo is corrupt or was written by an incompatible layout; decode surfaces it as InvalidInput at the range stage instead of panicking.

Source

Thrown at crates/ecstore/src/erasure/coding/decode.rs:1725

        length: usize,
        total_length: usize,
        read_costs: Option<Vec<ShardReadCost>>,
        deferred_handles: Vec<Option<DeferredReaderStripeHandle>>,
    ) -> (usize, Option<std::io::Error>)
    where
        W: AsyncWrite + Send + Sync + Unpin,
        R: crate::erasure::coding::ShardSource,
    {
        if readers.len() != self.data_shards + self.parity_shards {
            record_get_object_pipeline_failure(GET_STAGE_RANGE, GetObjectFailureReason::RangeOrLengthInvalid);
            return (0, Some(io::Error::new(ErrorKind::InvalidInput, "Invalid number of readers")));
        }

        // block_size/data_shards come from on-disk metadata; a corrupt FileInfo with a
        // zero here must surface as an error, not a divide-by-zero panic on every GET.
        if self.block_size == 0 || self.data_shards == 0 {
            record_get_object_pipeline_failure(GET_STAGE_RANGE, GetObjectFailureReason::RangeOrLengthInvalid);
            return (0, Some(io::Error::new(ErrorKind::InvalidInput, "Invalid erasure coding parameters")));
        }

        let Some(end_offset) = offset.checked_add(length) else {
            record_get_object_pipeline_failure(GET_STAGE_RANGE, GetObjectFailureReason::RangeOrLengthInvalid);
            return (0, Some(io::Error::new(ErrorKind::InvalidInput, "offset + length exceeds total length")));
        };
        if end_offset > total_length {
            record_get_object_pipeline_failure(GET_STAGE_RANGE, GetObjectFailureReason::RangeOrLengthInvalid);
            return (0, Some(io::Error::new(ErrorKind::InvalidInput, "offset + length exceeds total length")));
        }

        let mut ret_err = None;

        if length == 0 {
            return (0, ret_err);
        }

        let mut written = 0;

View on GitHub (pinned to 9e6e02ea09)

Solutions

  1. Dump the object's xl.meta and inspect ErasureInfo (block_size, data_shards)
  2. Heal the object so metadata is rebuilt from healthy shards, or restore from replication/tier
  3. If many objects show zeros, suspect the underlying disk (smartctl) and replace it
  4. Do not hand-edit xl.meta; use heal tooling to rewrite metadata
Defensive patterns

Strategy: fallback

Validate before calling

// Pre-read metadata validation
let fi = FileInfo::load(...)?;
if fi.erasure.block_size == 0 || fi.erasure.data_shards == 0 {
    return Err(corrupted_object(&path));
}

Type guard

fn is_zero_erasure_param(err: &std::io::Error) -> bool {
    err.kind() == std::io::ErrorKind::InvalidInput
        && err.to_string().contains("Invalid erasure coding parameters")
}

Try / catch

match get_object(...).await {
    Err(e) if is_zero_erasure_param(&e) => {
        // deterministic corruption: no retry; heal or restore from replica/tier
        heal_or_restore(&path).await
    }
    r => r,
}

Prevention

When it happens

Trigger: Reading an object whose xl.meta carries block_size=0 or data_shards=0: truncated metadata after a crash, disk corruption, or data directories copied between clusters with different erasure settings.

Common situations: Power loss during a metadata write; bit flips on failing drives; restoring data dirs onto a differently-configured pool.

Related errors


AI-assisted analysis of rustfs/rustfs@9e6e02ea09 (2026-08-16). Data as JSON: /api/errors/ce5a8d2033cca33a. Report an issue: GitHub.