quickwit-oss/quickwit · error
concatenate type must have at least one sub-field
Error message
concatenate type must have at least one sub-field
What it means
A `concatenate` field builds a combined searchable text from sub-fields. It must list at least one `concatenate_fields`, unless `include_dynamic_fields` is true (in which case dynamic fields provide the input). An empty list with dynamic fields disabled leaves the concatenate field with no source data, so deserialization bails.
Solutions
- Populate `concatenate_fields` with at least one text field name.
- Set `include_dynamic_fields: true` if you intend the concatenate field to cover dynamic fields.
- If neither applies, remove the concatenate field from the mapping.
Example fix
// before
{name: "all", type: "concatenate", concatenate_fields: [], include_dynamic_fields: false}
// after
{name: "all", type: "concatenate", concatenate_fields: ["title", "body"], include_dynamic_fields: false} Defensive patterns
Strategy: validation
Validate before calling
fn concatenate_has_sources(f: &serde_json::Value) -> bool {
if f.get("type").and_then(|t| t.as_str()) == Some("concatenate") {
let has_fields = f.get("concatenate_fields")
.and_then(|m| m.as_array())
.map_or(false, |a| !a.is_empty());
let dynamic = f.get("include_dynamic_fields")
.and_then(|v| v.as_bool()) == Some(true);
has_fields || dynamic
} else { true }
} Try / catch
match create_index_result {
Err(e) if e.to_string().contains("concatenate type must have at least one sub-field") => {
eprintln!("List concatenate_fields or enable include_dynamic_fields");
}
other => other?,
} Prevention
- Explicitly set include_dynamic_fields when you rely on dynamic fields for a concatenate field.
- Re-check concatenate_fields lists after removing source fields from the mapping.
- Lint mappings for empty concatenate_fields arrays.
When it happens
Trigger: A mapping like `{name: "all", type: "concatenate", concatenate_fields: [], include_dynamic_fields: false}` — FieldMappingType::Concatenate deserialization bails.
Common situations: Setting up a 'search everything' concatenate field but forgetting to list fields; auto-generated mappings with empty lists when the intended default was include_dynamic_fields=true; field names removed from the list leaving it empty.
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
- fast field is not allowed for array
- field name ` ` is reserved. the following fields are…
- object type must have at least one field mapping
- concatenate field uses an unknown field
- custom fields are not supported in Quickwit
AI-assisted analysis of quickwit-oss/quickwit@a39730c5cd (2026-09-08).
Data as JSON: /api/errors/c2c4f8058d6dbf99.
Report an issue: GitHub.
Appendix: source
Thrown at quickwit/quickwit-doc-mapper/src/doc_mapper/field_mapping_entry.rs:740
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))
}
Type::U64 => {
let numeric_options: QuickwitNumericOptions = serde_json::from_value(json)?;
Ok(FieldMappingType::U64(numeric_options, cardinality))
}
Type::I64 => {
let numeric_options: QuickwitNumericOptions = serde_json::from_value(json)?;
Ok(FieldMappingType::I64(numeric_options, cardinality))
}
Type::F64 => {View on GitHub (pinned to a39730c5cd)