quickwit-oss/tantivy · error
At least one bound must be set
Error message
At least one bound must be set
What it means
RangeFastFieldWeight::scorer (src/query/range_query/range_query_fastfield.rs:64) unwraps bounds.get_inner() with "At least one bound must be set" to obtain the term carrying the field and type; with no bounds set the panic fires. Like the other range queries, a boundless range is considered an invalid query object and fails fast at scoring time (also reachable via explain()).
Source
Thrown at src/query/range_query/range_query_fastfield.rs:64
impl FastFieldRangeWeight {
/// Create a new FastFieldRangeWeight
pub fn new(bounds: BoundsRange<Term>) -> Self {
Self { bounds }
}
}
impl Weight for FastFieldRangeWeight {
fn scorer(&self, reader: &SegmentReader, boost: Score) -> crate::Result<Box<dyn Scorer>> {
// Check if both bounds are Bound::Unbounded
if self.bounds.is_unbounded() {
return Ok(Box::new(AllScorer::new(reader.max_doc())));
}
let term = self
.bounds
.get_inner()
.expect("At least one bound must be set");
let schema = reader.schema();
let field_type = schema.get_field_entry(term.field()).field_type();
assert_eq!(
term.typ(),
field_type.value_type(),
"Field is of type {:?}, but got term of type {:?}",
field_type,
term.typ()
);
let field_name = term.get_full_path(reader.schema());
let get_value_bytes = |term: &Term| term.value().value_bytes_payload();
let term_value = term.value();
if field_type.is_json() {
let bounds = self
.bounds
.map_bound(|term| term.value().as_json_value_bytes().unwrap().to_owned());View on GitHub (pinned to b5d8deb80c)
Solutions
- Set at least one bound before executing or explaining the query.
- Check bounds in the builder layer and return a user-facing error instead of building the query object.
- For "match everything" semantics use the all-query or the documented unbounded range constructors rather than an empty range.
Example fix
// before
let weight = fast_field_range_query.weight(...)?;
let scorer = weight.scorer(&reader, 1.0)?; // panics if bounds empty
// after
if bounds.lower_bound.is_none() && bounds.upper_bound.is_none() {
return Ok(Box::new(AllScorer::new(reader.max_doc()))); // or return Err(...)
}
let scorer = weight.scorer(&reader, 1.0)?; Defensive patterns
Strategy: validation
Validate before calling
if bounds.lower_bound.is_none() && bounds.upper_bound.is_none() {
// fall back to AllScorer or return an error before scoring
} Prevention
- Set at least one bound before weight()/scorer()/explain()
- Unit-test dynamically constructed fast-field range queries
- Use documented unbounded-range helpers, never empty bounds
- Wrap user query building in validation returning Result
When it happens
Trigger: Calling weight(...).scorer(...) or explain() on a fast-field range query whose RangeBounds never had lower_bound/upper_bound set; also hit from tests like test_range_regression_simplified when constructing the weight manually.
Common situations: Programmatic construction of fast-field range queries with both bound branches skipped; empty range objects from parsed/deserialized queries; copy-pasted builder code where setter calls were removed.
Related errors
- expected json type in term
- At least one bound must be set
- FastFieldsPlugin is a built-in; use FastFieldsPluginWriter::
- PhrasePrefixScorer must have at least two terms
- unknown compressor id {id:?}
AI-assisted analysis of quickwit-oss/tantivy@b5d8deb80c (2026-09-05).
Data as JSON: /api/errors/9a69fa38b432c664.
Report an issue: GitHub.