quickwit-oss/tantivy · error
At least one bound must be set
Error message
At least one bound must be set
What it means
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.
Source
Thrown at src/query/range_query/range_query.rs:99
RangeQuery {
bounds: BoundsRange::new(lower_bound, upper_bound),
}
}
/// Field to search over
pub fn field(&self) -> Field {
self.get_term().field()
}
/// The value type of the field
pub fn value_type(&self) -> Type {
self.get_term().typ()
}
pub(crate) fn get_term(&self) -> &Term {
self.bounds
.get_inner()
.expect("At least one bound must be set")
}
}
impl Query for RangeQuery {
fn weight(&self, enable_scoring: EnableScoring<'_>) -> crate::Result<Box<dyn Weight>> {
let schema = enable_scoring.schema();
let field_type = schema.get_field_entry(self.field()).field_type();
if field_type.is_fast() && is_type_valid_for_fastfield_range_query(self.value_type()) {
Ok(Box::new(FastFieldRangeWeight::new(self.bounds.clone())))
} else {
if field_type.is_json() {
return Err(crate::TantivyError::InvalidArgument(
"RangeQuery on JSON is only supported for fast fields currently".to_string(),
));
}
Ok(Box::new(InvertedIndexRangeWeight::new(
self.field(),View on GitHub (pinned to b5d8deb80c)
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.
Example fix
// before
let query = RangeQuery::new_term_range(field, bounds); // bounds has neither lower nor upper
let top = searcher.search(&query, &collector)?; // panics
// after
if bounds.lower_bound.is_none() && bounds.upper_bound.is_none() {
return Err(TantivyError::InvalidArgument("range query needs at least one bound".into()));
}
let top = searcher.search(&query, &collector)?; Defensive patterns
Strategy: validation
Validate before calling
fn has_any_bound(b: &RangeBounds) -> bool {
b.lower_bound.is_some() || b.upper_bound.is_some()
}
if !has_any_bound(&bounds) { return Err(...); } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- At least one bound must be set
- expected json type in term
- PhrasePrefixScorer must have at least two terms
- unknown compressor id {id:?}
- actual doc store version: {doc_store_version}, max_supported
AI-assisted analysis of quickwit-oss/tantivy@b5d8deb80c (2026-09-05).
Data as JSON: /api/errors/262f2f6f0965c935.
Report an issue: GitHub.