quickwit-oss/tantivy · error
expected json type in term
Error message
expected json type in term
What it means
RangeFastFieldWeight::scorer converts the range-bound term into JSON value bytes via term_value.as_json_value_bytes().expect("expected json type in term") (src/query/range_query/range_query_fastfield.rs:90). This expects the bound term to carry JSON-encoded value bytes; a term whose type is not JSON-encoded makes the conversion None and panics. It occurs when a fast-field range query is built with bound terms created by a non-JSON path, so the term type disagrees with the JSON column handling.
Source
Thrown at src/query/range_query/range_query_fastfield.rs:90
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());
// Unlike with other field types JSON may have multiple columns of different types
// under the same name
//
// In the JSON case the provided type in term may not exactly match the column type,
// especially with the numeric type interpolation
let json_value_bytes = term_value
.as_json_value_bytes()
.expect("expected json type in term");
let typ = json_value_bytes.typ();
match typ {
Type::Str => {
let Some(str_dict_column): Option<StrColumn> =
reader.fast_fields().str(&field_name)?
else {
return Ok(Box::new(EmptyScorer));
};
let dict = str_dict_column.dictionary();
let bounds = self.bounds.map_bound(get_value_bytes);
// Get term ids for terms
let (lower_bound, upper_bound) =
dict.term_bounds_to_ord(bounds.lower_bound, bounds.upper_bound)?;
let fast_field_reader = reader.fast_fields();
let Some((column, _col_type)) = fast_field_reader
.u64_lenient_for_type(Some(&[ColumnType::Str]), &field_name)?View on GitHub (pinned to b5d8deb80c)
Solutions
- Create the bound terms with the JSON-aware constructors (a Type::Json term with append_type_and_fast_value) so the value bytes carry a JSON type prefix.
- Verify the schema: ensure the queried field is actually the JSON/fast-field type you assume, and that term.typ() matches field_type.value_type() (the assert above this line checks it).
- If bounds come from user input, convert them with the documented range bound APIs (date/u64/i64/f64 helpers) instead of manual Term bytes.
- Rebuild queries after schema migrations; do not reuse Term objects built for the old field type.
Example fix
// before let term = Term::from_field_u64(field, 42); // non-JSON bytes let query = make_fast_field_range(field, term); // panics in scorer // after let mut term = Term::with_type_and_field(Type::Json, field); term.append_type_and_fast_value(42u64); // JSON-encoded value bytes let query = make_fast_field_range(field, term);
Defensive patterns
Strategy: validation
Validate before calling
fn is_json_term(t: &Term) -> bool { t.typ() == Type::Json }
if !is_json_term(&bound_term) { return Err(TantivyError::InvalidArgument("bound must be a JSON term".into())); } Type guard
fn as_json_term(t: &Term) -> Option<&Term> {
(t.typ() == Type::Json).then_some(t)
} Prevention
- Build bound terms with JSON-aware constructors (append_type_and_fast_value on a Type::Json term)
- Confirm the field is a JSON fast field before using fast-field range queries
- Don't reuse primitive-typed Terms after schema migrations
- Rely on the documented range bound APIs instead of raw Term bytes
When it happens
Trigger: Building a fast-field range query where the bound Term was constructed for a non-JSON field type (e.g. Term::from_field_u64 on a field handled as a JSON fast field), so as_json_value_bytes returns None at scorer()/explain() time.
Common situations: Mismatch between the query's declared field type and the term factory used (schema evolved from primitive to JSON fast field); hand-built Terms in custom query code; version changes where fast-field range queries started requiring JSON-encoded bound terms.
Related errors
- At least one bound must be set
- At least one bound must be set
- FastFieldsPlugin is a built-in; use FastFieldsPluginWriter::
- All columns re required to be numerical
- unexpected metric type
AI-assisted analysis of quickwit-oss/tantivy@b5d8deb80c (2026-09-05).
Data as JSON: /api/errors/9bfc43b8dde55f0f.
Report an issue: GitHub.