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

  1. Add at least one field mapping inside the object's `field_mappings`.
  2. If the object is not needed, remove the object field entirely instead of leaving it empty.
  3. 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

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


AI-assisted analysis of quickwit-oss/quickwit@a39730c5cd (2026-09-08). Data as JSON: /api/errors/d4145501a6bb4eea. Report an issue: GitHub.