rustfs/rustfs · error · io::Error
decoded stripe is missing a data shard
Error message
decoded stripe is missing a data shard
What it means
While emitting a decoded block on the streaming read path, every one of the first data_shards stripe slots must hold bytes. A None slot after reconstruction means the erasure decode could not rebuild that data shard: too few healthy shards were available (read quorum lost) or bitrot verification dropped shards during this stripe's fetch.
Source
Thrown at crates/ecstore/src/erasure/coding/decode_reader.rs:806
};
rustfs_io_metrics::record_get_object_reconstruct_outcome(metrics_path, engine.engine_name(), reconstruct_outcome);
record_get_stage_duration_if_enabled(metrics_path, GET_STAGE_RECONSTRUCT, reconstruct_stage_start);
if state.shards_mut().len() < engine.data_shards() {
return Err(io::Error::new(
ErrorKind::UnexpectedEof,
"decoded stripe has fewer shards than data shard count",
));
}
let emit_stage_start = get_stage_timer_if_enabled(stage_metrics_enabled);
reserve_output_capacity(output, engine.block_size().min(remaining));
for shard in state.shards_mut().iter().take(engine.data_shards()) {
if output.len() >= remaining {
break;
}
let Some(shard) = shard else {
return Err(io::Error::new(ErrorKind::UnexpectedEof, "decoded stripe is missing a data shard"));
};
let copy_len = shard.len().min(remaining - output.len());
output.extend_from_slice(&shard[..copy_len]);
}
record_get_stage_duration_if_enabled(metrics_path, GET_STAGE_EMIT, emit_stage_start);
Ok(true)
}
#[allow(dead_code, reason = "shard emission asserted by this file's tests (backlog#1823)")]
fn emit_data_shards(state: &StripeReadState, data_shards: usize, block_size: usize, remaining: usize) -> io::Result<Vec<u8>> {
let mut output = Vec::new();
emit_data_shards_into(state, data_shards, block_size, remaining, &mut output)?;
Ok(output)
}
fn reserve_output_capacity(output: &mut Vec<u8>, target_capacity: usize) {
if output.capacity() < target_capacity {View on GitHub (pinned to 9e6e02ea09)
Solutions
- Check set health and bring offline drives back (remount disks, fix network)
- Inspect disks for errors (dmesg, SMART) and replace failing ones
- Run rustfs-admin heal for the affected bucket/object
- Confirm the read quorum: online and healthy drives must be at least data_shards before reads can succeed
Defensive patterns
Strategy: try-catch
Validate before calling
// preflight: quorum must be readable before starting the GET
let healthy = set.disks().iter().filter(|d| d.is_online()).count();
if healthy < set.data_shards() {
return Err(read_quorum_lost(healthy, set.data_shards()));
} Try / catch
match res {
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof && e.to_string().contains("missing a data shard") => {
// respond 503/500, trigger heal for the object, surface drive health
}
other => other,
} Prevention
- Keep online healthy drives >= data_shards at all times; alert before parity is exhausted
- Run periodic heal so missing shards are rebuilt proactively
When it happens
Trigger: GET when the number of failed, unreachable, or corrupt drives exceeds parity_shards; a data-shard read failed bitrot or IO verification during this stripe, leaving a None slot reconstruction cannot fill.
Common situations: Multiple dead disks in one erasure set; network flap making remote endpoints unreachable; bitrot on data parts detected mid-read.
Related errors
- UnexpectedEof
- data size {want} exceeds shard size {}
- short shard read: got {got} of {want} bytes
- bitrot hash mismatch
- bitrot writer already finished
AI-assisted analysis of rustfs/rustfs@9e6e02ea09 (2026-08-16).
Data as JSON: /api/errors/4a9529d70bb756ac.
Report an issue: GitHub.