quickwit-oss/quickwit · error

Failed to serialize QueryAst

Error message

Failed to serialize QueryAst

What it means

When translating an Elasticsearch search request into a gRPC SearchRequest, the parsed QueryAst is serialized to a JSON string stored in the query_ast field. Serialization of QueryAst is an invariant, so serde_json::to_string failing triggers this expect panic.

Source

Thrown at quickwit/quickwit-serve/src/elasticsearch_api/rest_handler.rs:572

        })
        .take_while_inclusive(|sort_field| !is_doc_field(sort_field))
        .collect();
    if sort_fields.len() >= 3 {
        return Err(ElasticsearchError::from(SearchError::InvalidArgument(
            format!("only up to two sort fields supported at the moment. got {sort_fields:?}"),
        )));
    }

    let scroll_duration: Option<Duration> = search_params.parse_scroll_ttl()?;
    let scroll_ttl_secs: Option<u32> = scroll_duration.map(|duration| duration.as_secs() as u32);

    let has_doc_id_field = sort_fields.iter().any(is_doc_field);
    let search_after = partial_hit_from_search_after_param(search_body.search_after, &sort_fields)?;

    Ok((
        quickwit_proto::search::SearchRequest {
            index_id_patterns,
            query_ast: serde_json::to_string(&query_ast).expect("Failed to serialize QueryAst"),
            max_hits,
            start_offset,
            aggregation_request,
            sort_fields,
            start_timestamp: None,
            end_timestamp: None,
            snippet_fields: Vec::new(),
            scroll_ttl_secs,
            search_after,
            count_hits,
            ignore_missing_indexes,
            skip_aggregation_finalization: false,
            ..Default::default()
        },
        has_doc_id_field,
    ))
}

View on GitHub (pinned to a39730c5cd)

Solutions

  1. Verify the incoming ES query body parses to a standard QueryAst (test with a minimal query)
  2. Align quickwit-query versions across the build
  3. If you extended QueryAst, implement/derive Serialize correctly and round-trip test
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate the ES query body is standard and JSON-serializable:
serde_json::to_string(&parsed_query_ast).map_err(|e| ElasticsearchError::from(e))?;

Try / catch

// Wrap the ES search call:
let resp = client.search_v2(request).await?; // server-side expect; on panic, check server logs
// Client side use:
match result { Ok(v) => v, Err(e) => { log::error!("search failed: {e}"); return Err(e); } }

Prevention

When it happens

Trigger: convert_search_query_from_es_api (constructing quickwit_proto::search::SearchRequest) receives a QueryAst whose serde serialization fails, e.g. from a newly added AST variant missing Serialize support.

Common situations: Workspace version skew of quickwit-query; custom query AST extensions; corrupted query payloads from ES-compatible clients that deserialize but fail to re-serialize.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


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