{"record":{"id":"a3fdeac61a3e886e","repo":"quickwit-oss/tantivy","slug":"internal-error-called-get-val-of-empty-column","errorCode":null,"errorMessage":"Internal Error: Called get_val of empty column.","messagePattern":"Internal Error: Called get_val of empty column\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"columnar/src/column_values/mod.rs","lineNumber":184,"sourceCode":"    /// ∃i < self.num_vals(), self.get_val(i) == self.max_value()\n    fn max_value(&self) -> T;\n\n    /// The number of values in the column.\n    fn num_vals(&self) -> u32;\n\n    /// Returns a iterator over the data\n    fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = T> + 'a> {\n        Box::new((0..self.num_vals()).map(|idx| self.get_val(idx)))\n    }\n}\ndowncast_rs::impl_downcast!(sync ColumnValues<T> where T: PartialOrd);\n\n/// Empty column of values.\npub struct EmptyColumnValues;\n\nimpl<T: PartialOrd + Default> ColumnValues<T> for EmptyColumnValues {\n    fn get_val(&self, _idx: u32) -> T {\n        panic!(\"Internal Error: Called get_val of empty column.\")\n    }\n\n    fn min_value(&self) -> T {\n        T::default()\n    }\n\n    fn max_value(&self) -> T {\n        T::default()\n    }\n\n    fn num_vals(&self) -> u32 {\n        0\n    }\n}\n\nimpl<T: Copy + PartialOrd + Debug + 'static> ColumnValues<T> for Arc<dyn ColumnValues<T>> {\n    #[inline(always)]\n    fn get_val(&self, idx: u32) -> T {","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/quickwit-oss/tantivy/blob/b5d8deb80c26924e6b007a5b1a7630f35ca64de4/columnar/src/column_values/mod.rs#L166-L202","documentation":"EmptyColumnValues is a sentinel implementation of ColumnValues for a column with zero rows. Its get_val always panics because there is no value at any index. It is intentional: random access into an empty column is undefined and callers are expected to use min_value/max_value or empty iteration instead.","triggerScenarios":"Calling get_val(idx) (directly or via get_vals, get_vals_opt, get_range, get_row_ids_for_value_range, or iter) on a column backed by EmptyColumnValues — i.e. any column with 0 rows — with any index.","commonSituations":"Querying/collecting over an empty segment or an empty table shard; a filter eliminated all docs before aggregation reads values; opening a column family that exists but has no rows; code that iterates columns by doc_id range without checking num_vals() == 0.","solutions":["Check column.num_vals() (or doc count) before calling get_val/get_vals and skip empty columns.","Use get_vals_opt / iter which tolerate empty columns, or rely on min_value/max_value defaults for statistics.","Guard range queries: if the requested range overlaps no rows, return an empty result without touching values."],"exampleFix":"// before\nlet val = column.values().get_val(doc_id);\n// after\nif column.num_vals() == 0 {\n    return Vec::new(); // empty column has no values\n}\nlet val = column.values().get_val(doc_id);","handlingStrategy":"validation","validationCode":"if column.num_vals() == 0 {\n    return Ok(Vec::new());\n}\nlet val = column.values().get_val(idx);","typeGuard":"fn is_empty_column<T>(col: &dyn ColumnValues<T>) -> bool {\n    col.num_vals() == 0\n}","tryCatchPattern":"// panics are not catchable in Rust; guard instead\nassert!(column.num_vals() > 0, \"cannot read value from empty column\");","preventionTips":["Always check num_vals()/doc count before random access","Prefer get_vals_opt or iteration over direct get_val","Handle empty segments/shards as a first-class case in collectors"],"tags":["panic","columnar","empty-column","bounds","data-access"],"backgroundTag":"empty-column-random-access","analyzedSha":"b5d8deb80c26924e6b007a5b1a7630f35ca64de4","analyzedAt":"2026-09-05T13:20:51.521Z","contentChangedAt":"2026-09-05T13:20:51.521Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}