{"record":{"id":"822f2c82135e3dc4","repo":"quickwit-oss/quickwit","slug":"failed-to-serialize-queryast","errorCode":null,"errorMessage":"Failed to serialize QueryAst","messagePattern":"Failed to serialize QueryAst","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"quickwit/quickwit-serve/src/elasticsearch_api/rest_handler.rs","lineNumber":572,"sourceCode":"        })\n        .take_while_inclusive(|sort_field| !is_doc_field(sort_field))\n        .collect();\n    if sort_fields.len() >= 3 {\n        return Err(ElasticsearchError::from(SearchError::InvalidArgument(\n            format!(\"only up to two sort fields supported at the moment. got {sort_fields:?}\"),\n        )));\n    }\n\n    let scroll_duration: Option<Duration> = search_params.parse_scroll_ttl()?;\n    let scroll_ttl_secs: Option<u32> = scroll_duration.map(|duration| duration.as_secs() as u32);\n\n    let has_doc_id_field = sort_fields.iter().any(is_doc_field);\n    let search_after = partial_hit_from_search_after_param(search_body.search_after, &sort_fields)?;\n\n    Ok((\n        quickwit_proto::search::SearchRequest {\n            index_id_patterns,\n            query_ast: serde_json::to_string(&query_ast).expect(\"Failed to serialize QueryAst\"),\n            max_hits,\n            start_offset,\n            aggregation_request,\n            sort_fields,\n            start_timestamp: None,\n            end_timestamp: None,\n            snippet_fields: Vec::new(),\n            scroll_ttl_secs,\n            search_after,\n            count_hits,\n            ignore_missing_indexes,\n            skip_aggregation_finalization: false,\n            ..Default::default()\n        },\n        has_doc_id_field,\n    ))\n}\n","sourceCodeStart":554,"sourceCodeEnd":590,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-serve/src/elasticsearch_api/rest_handler.rs#L554-L590","documentation":"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.","triggerScenarios":"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.","commonSituations":"Workspace version skew of quickwit-query; custom query AST extensions; corrupted query payloads from ES-compatible clients that deserialize but fail to re-serialize.","solutions":["Verify the incoming ES query body parses to a standard QueryAst (test with a minimal query)","Align quickwit-query versions across the build","If you extended QueryAst, implement/derive Serialize correctly and round-trip test"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-validate the ES query body is standard and JSON-serializable:\nserde_json::to_string(&parsed_query_ast).map_err(|e| ElasticsearchError::from(e))?;","typeGuard":null,"tryCatchPattern":"// Wrap the ES search call:\nlet resp = client.search_v2(request).await?; // server-side expect; on panic, check server logs\n// Client side use:\nmatch result { Ok(v) => v, Err(e) => { log::error!(\"search failed: {e}\"); return Err(e); } }","preventionTips":["Use only documented ES-compatible query DSL fields","Add round-trip (to_string -> from_str) tests for any new QueryAst node","Avoid unsupported custom AST payloads from third-party clients"],"tags":["rust","serialization","json","panic","query-ast"],"backgroundTag":"json-marshal-failed","analyzedSha":"a39730c5cdcd1a4fe798403737ae293999ea21f8","analyzedAt":"2026-09-08T13:19:37.784Z","contentChangedAt":"2026-09-08T13:19:37.784Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}