quickwit-oss/quickwit · error · anyhow::Error
object type must have at least one field mapping
Error message
object type must have at least one field mapping
What it means
A field of type `object` in a Quickwit mapping must declare its nested `field_mappings`. When deserializing the object options, an empty field_mappings list means the object contributes nothing to the schema, so FieldMappingType deserialization fails. This prevents empty schema branches.
Source
Thrown at quickwit/quickwit-doc-mapper/src/doc_mapper/field_mapping_entry.rs:731
.set_fieldnorms(quickwit_text_options.indexing_options.fieldnorms)
.set_tokenizer(quickwit_text_options.indexing_options.tokenizer.name());
text_options = text_options.set_indexing_options(text_field_indexing);
text_options
}
}
fn deserialize_mapping_type(
quickwit_field_type: QuickwitFieldType,
json: JsonValue,
) -> anyhow::Result<FieldMappingType> {
let (typ, cardinality) = match quickwit_field_type {
QuickwitFieldType::Simple(typ) => (typ, Cardinality::SingleValued),
QuickwitFieldType::Array(typ) => (typ, Cardinality::MultiValued),
QuickwitFieldType::Object => {
let object_options: QuickwitObjectOptions = serde_json::from_value(json)?;
if object_options.field_mappings.is_empty() {
anyhow::bail!("object type must have at least one field mapping");
}
return Ok(FieldMappingType::Object(object_options));
}
QuickwitFieldType::Concatenate => {
let concatenate_options: QuickwitConcatenateOptions = serde_json::from_value(json)?;
if concatenate_options.concatenate_fields.is_empty()
&& !concatenate_options.include_dynamic_fields
{
anyhow::bail!("concatenate type must have at least one sub-field");
}
return Ok(FieldMappingType::Concatenate(concatenate_options));
}
};
match typ {
Type::Str => {
let text_options: QuickwitTextOptions = serde_json::from_value(json)?;
Ok(FieldMappingType::Text(text_options, cardinality))
}View on GitHub (pinned to a39730c5cd)
Solutions
- Add at least one field mapping inside the object's `field_mappings`.
- If the object is not needed, remove the object field entirely instead of leaving it empty.
- If nested fields are optional at creation, build the mapping only once you know the concrete sub-fields.
Example fix
// before
{name: "user", type: "object", field_mappings: []}
// after
{name: "user", type: "object", field_mappings: [{name: "id", type: "u64"}]} Defensive patterns
Strategy: validation
Validate before calling
fn object_has_children(f: &serde_json::Value) -> bool {
f.get("type").and_then(|t| t.as_str()) != Some("object")
|| f.get("field_mappings")
.and_then(|m| m.as_array())
.map_or(false, |a| !a.is_empty())
} Try / catch
match create_index_result {
Err(e) if e.to_string().contains("at least one field mapping") => {
eprintln!("Add nested field_mappings or remove the empty object field");
}
other => other?,
} Prevention
- Only emit object fields once their sub-fields are known.
- Validate generated mappings programmatically before submission.
- Remove placeholder object fields from templates.
When it happens
Trigger: Submitting an index config with `{name: "parent", type: "object", field_mappings: []}` — serde deserialization of FieldMappingType::Object via from_json bails.
Common situations: Programmatic mapping generation that starts with an empty field list; hand-written mappings where nested fields were accidentally removed; templating that emits `field_mappings: []` as a placeholder.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- concatenate type must have at least one sub-field
- fast field is not allowed for array<bytes>
- field name `{field_mapping_name}` is reserved. the following
- `record`, `tokenizer`, and `fieldnorms` parameters are allow
- `record` and `tokenizer` parameters are allowed only if inde
AI-assisted analysis of quickwit-oss/quickwit@a39730c5cd (2026-09-08).
Data as JSON: /api/errors/d4145501a6bb4eea.
Report an issue: GitHub.