{"record":{"id":"262f2f6f0965c935","repo":"quickwit-oss/tantivy","slug":"at-least-one-bound-must-be-set","errorCode":null,"errorMessage":"At least one bound must be set","messagePattern":"At least one bound must be set","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/query/range_query/range_query.rs","lineNumber":99,"sourceCode":"        RangeQuery {\n            bounds: BoundsRange::new(lower_bound, upper_bound),\n        }\n    }\n\n    /// Field to search over\n    pub fn field(&self) -> Field {\n        self.get_term().field()\n    }\n\n    /// The value type of the field\n    pub fn value_type(&self) -> Type {\n        self.get_term().typ()\n    }\n\n    pub(crate) fn get_term(&self) -> &Term {\n        self.bounds\n            .get_inner()\n            .expect(\"At least one bound must be set\")\n    }\n}\n\nimpl Query for RangeQuery {\n    fn weight(&self, enable_scoring: EnableScoring<'_>) -> crate::Result<Box<dyn Weight>> {\n        let schema = enable_scoring.schema();\n        let field_type = schema.get_field_entry(self.field()).field_type();\n\n        if field_type.is_fast() && is_type_valid_for_fastfield_range_query(self.value_type()) {\n            Ok(Box::new(FastFieldRangeWeight::new(self.bounds.clone())))\n        } else {\n            if field_type.is_json() {\n                return Err(crate::TantivyError::InvalidArgument(\n                    \"RangeQuery on JSON is only supported for fast fields currently\".to_string(),\n                ));\n            }\n            Ok(Box::new(InvertedIndexRangeWeight::new(\n                self.field(),","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/quickwit-oss/tantivy/blob/b5d8deb80c26924e6b007a5b1a7630f35ca64de4/src/query/range_query/range_query.rs#L81-L117","documentation":"RangeQuery::get_term (src/query/range_query/range_query.rs:99) unwraps bounds.get_inner(), which is Some only when at least one of lower_bound/upper_bound has been set. A RangeQuery constructed with no bounds at all panics here with \"At least one bound must be set\". A range without any bound is meaningless, so tantivy treats it as a programmer error rather than a Result.","triggerScenarios":"Building a RangeQuery via RangeQueryBuilder (or the struct directly) and calling weight()/field()/value_type()/get_term() without ever calling the lower_bound or upper_bound setters (greater_than/less_than family). Executing such a query via searcher.search() triggers weight() and thus the panic.","commonSituations":"Programmatic query builders where both bound-setting calls are behind conditional branches that both evaluated false; deserializing query JSON like {\"range\":{\"price\":{}}} with an empty range object into a RangeQuery; refactors that renamed bound setters so they silently stopped being called.","solutions":["Set at least one bound before executing: call lower_bound or upper_bound (or the greater_than/less_than builder methods).","For open-ended ranges, use the documented unbounded helpers or a MatchAll query instead of an empty range.","Validate deserialized query JSON: reject range objects whose bounds map is empty before converting to RangeQuery.","If bounds are dynamic, fall back to a different query type when no bound is provided."],"exampleFix":"// before\nlet query = RangeQuery::new_term_range(field, bounds); // bounds has neither lower nor upper\nlet top = searcher.search(&query, &collector)?; // panics\n\n// after\nif bounds.lower_bound.is_none() && bounds.upper_bound.is_none() {\n    return Err(TantivyError::InvalidArgument(\"range query needs at least one bound\".into()));\n}\nlet top = searcher.search(&query, &collector)?;","handlingStrategy":"validation","validationCode":"fn has_any_bound(b: &RangeBounds) -> bool {\n    b.lower_bound.is_some() || b.upper_bound.is_some()\n}\nif !has_any_bound(&bounds) { return Err(...); }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always call at least one bound setter on the builder","Reject empty range clauses in parsed query JSON","Use explicit unbounded/all queries instead of empty ranges","Add a unit test for dynamically built ranges"],"tags":["rust","panic","query","range-query","validation"],"backgroundTag":"empty-range-bounds","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"}