{"record":{"id":"4a9529d70bb756ac","repo":"rustfs/rustfs","slug":"decoded-stripe-is-missing-a-data-shard","errorCode":null,"errorMessage":"decoded stripe is missing a data shard","messagePattern":"decoded stripe is missing a data shard","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/ecstore/src/erasure/coding/decode_reader.rs","lineNumber":806,"sourceCode":"    };\n    rustfs_io_metrics::record_get_object_reconstruct_outcome(metrics_path, engine.engine_name(), reconstruct_outcome);\n    record_get_stage_duration_if_enabled(metrics_path, GET_STAGE_RECONSTRUCT, reconstruct_stage_start);\n\n    if state.shards_mut().len() < engine.data_shards() {\n        return Err(io::Error::new(\n            ErrorKind::UnexpectedEof,\n            \"decoded stripe has fewer shards than data shard count\",\n        ));\n    }\n\n    let emit_stage_start = get_stage_timer_if_enabled(stage_metrics_enabled);\n    reserve_output_capacity(output, engine.block_size().min(remaining));\n    for shard in state.shards_mut().iter().take(engine.data_shards()) {\n        if output.len() >= remaining {\n            break;\n        }\n        let Some(shard) = shard else {\n            return Err(io::Error::new(ErrorKind::UnexpectedEof, \"decoded stripe is missing a data shard\"));\n        };\n        let copy_len = shard.len().min(remaining - output.len());\n        output.extend_from_slice(&shard[..copy_len]);\n    }\n    record_get_stage_duration_if_enabled(metrics_path, GET_STAGE_EMIT, emit_stage_start);\n\n    Ok(true)\n}\n\n#[allow(dead_code, reason = \"shard emission asserted by this file's tests (backlog#1823)\")]\nfn emit_data_shards(state: &StripeReadState, data_shards: usize, block_size: usize, remaining: usize) -> io::Result<Vec<u8>> {\n    let mut output = Vec::new();\n    emit_data_shards_into(state, data_shards, block_size, remaining, &mut output)?;\n    Ok(output)\n}\n\nfn reserve_output_capacity(output: &mut Vec<u8>, target_capacity: usize) {\n    if output.capacity() < target_capacity {","sourceCodeStart":788,"sourceCodeEnd":824,"githubUrl":"https://github.com/rustfs/rustfs/blob/9e6e02ea09c86bedf44c7bd64a74ea02a0cff1de/crates/ecstore/src/erasure/coding/decode_reader.rs#L788-L824","documentation":"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.","triggerScenarios":"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.","commonSituations":"Multiple dead disks in one erasure set; network flap making remote endpoints unreachable; bitrot on data parts detected mid-read.","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"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// preflight: quorum must be readable before starting the GET\nlet healthy = set.disks().iter().filter(|d| d.is_online()).count();\nif healthy < set.data_shards() {\n    return Err(read_quorum_lost(healthy, set.data_shards()));\n}","typeGuard":null,"tryCatchPattern":"match res {\n    Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof && e.to_string().contains(\"missing a data shard\") => {\n        // respond 503/500, trigger heal for the object, surface drive health\n    }\n    other => other,\n}","preventionTips":["Keep online healthy drives >= data_shards at all times; alert before parity is exhausted","Run periodic heal so missing shards are rebuilt proactively"],"tags":["erasure-coding","unexpected-eof","read-quorum","bitrot"],"backgroundTag":"read-quorum-lost","analyzedSha":"9e6e02ea09c86bedf44c7bd64a74ea02a0cff1de","analyzedAt":"2026-08-16T20:34:17.560Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}