{"record":{"id":"b87d34acf5cb8764","repo":"databendlabs/databend","slug":"internal-error-entered-unreachable-code","errorCode":null,"errorMessage":"internal error: entered unreachable code","messagePattern":"internal error: entered unreachable code","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/query/pipeline/transforms/src/processors/transforms/sorts/sort_spill.rs","lineNumber":1077,"sourceCode":"pub type MemoryMerger<A> = Merger<A, DataBlockStream>;\n\npub fn create_memory_merger<A: SortAlgorithm>(\n    blocks: Vec<DataBlock>,\n    sort_row_offset: usize,\n    limit: Option<usize>,\n    batch_rows: usize,\n) -> MemoryMerger<A> {\n    let streams = blocks\n        .into_iter()\n        .map(|data| DataBlockStream::new(data, sort_row_offset))\n        .collect();\n    Merger::<A, _>::new(streams, batch_rows, limit)\n}\n\nfn get_domain(entry: &BlockEntry) -> Column {\n    match entry {\n        BlockEntry::Column(col) => match col.len() {\n            0 => unreachable!(),\n            1 | 2 => col.clone(),\n            n => {\n                let mut bitmap = MutableBitmap::with_capacity(n);\n                bitmap.push(true);\n                bitmap.extend_constant(n - 2, false);\n                bitmap.push(true);\n\n                col.filter(&bitmap.freeze())\n            }\n        },\n        BlockEntry::Const(scalar, data_type, n) => match n {\n            0 => unreachable!(),\n            1 => BlockEntry::new_const_column(data_type.clone(), scalar.clone(), 1).to_column(),\n            _ => BlockEntry::new_const_column(data_type.clone(), scalar.clone(), 2).to_column(),\n        },\n    }\n}\n","sourceCodeStart":1059,"sourceCodeEnd":1095,"githubUrl":"https://github.com/databendlabs/databend/blob/288d84d76e20a2f8f7173bda9691eb6ece301aa9/src/query/pipeline/transforms/src/processors/transforms/sorts/sort_spill.rs#L1059-L1095","documentation":"This is a Rust `unreachable!()` panic inside `get_domain` in sort_spill.rs, which shrinks a spilled/merged block's columns to a 3-row domain for merge sorting. The code declares a 0-length column impossible in the spill-merge path and panics via `unreachable!()` instead of returning an error. It means the internal invariant 'every block entry column in a spilled batch has at least 1 row' was violated.","triggerScenarios":"The sort spill merger builds block domains from spilled blocks and encounters a `BlockEntry::Column` whose column length is 0 — e.g. an empty block got spilled/serialized into the merge input streams, or a columnar entry with zero rows survived block filtering before `Merger::new`.","commonSituations":"Empty partition data being spilled during a large ORDER BY (spilling an empty block after filtering), a bug/edge case in block splitting where a 0-row block enters the merge, or corrupted/round-tripped spill files producing empty columns.","solutions":["Ensure empty blocks are never spilled: filter out blocks with `block.is_empty()` / num_rows == 0 before writing to the spill location.","Upgrade to a Databend version where the spill merger skips 0-row blocks in `get_domain`.","If reproducible, capture the query plan and spilled-file metadata and file an issue with the query that triggered empty-column spill.","As a workaround, reduce spill pressure (raise memory limit, reduce sort volume) so the merge path is not fed empty blocks."],"exampleFix":"// before (sort_spill.rs, get_domain)\nBlockEntry::Column(col) => match col.len() {\n    0 => unreachable!(),\n    1 | 2 => col.clone(),\n    ...\n}\n// after\nBlockEntry::Column(col) => match col.len() {\n    0 => return Ok(Column::full_default(0, /* col data type */)), // or skip this block entirely\n    1 | 2 => col.clone(),\n    ...\n}","handlingStrategy":"validation","validationCode":"// caller-side: never spill empty blocks into the sort merge streams\nif block.num_rows() == 0 { return Ok(None); } // skip before passing to spill/Merger::new","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Filter 0-row blocks before spill and merge.","Pin a Databend version with empty-block guards in sort_spill.","Monitor for 'unreachable' panics in logs and attach query plans when reporting.","Raise memory limits in tests so spill paths are exercised deliberately, not accidentally."],"tags":["rust","panic","spill","sort","internal-invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"288d84d76e20a2f8f7173bda9691eb6ece301aa9","analyzedAt":"2026-09-11T11:29:36.208Z","contentChangedAt":"2026-09-11T11:29:36.208Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}