{"record":{"id":"f0df9c50c51b989d","repo":"quickwit-oss/quickwit","slug":"queryast-should-be-json-serializable","errorCode":null,"errorMessage":"QueryAst should be JSON serializable","messagePattern":"QueryAst should be JSON serializable","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"quickwit/quickwit-serve/src/elasticsearch_api/model/field_capability.rs","lineNumber":224,"sourceCode":"        ElasticsearchError::new(\n            StatusCode::BAD_REQUEST,\n            format!(\"Failed to convert index_filter: {err}\"),\n            None,\n        )\n    })?;\n\n    Ok(Some(query_ast))\n}\n\n#[allow(clippy::result_large_err)]\npub fn build_list_field_request_for_es_api(\n    index_id_patterns: Vec<String>,\n    search_params: FieldCapabilityQueryParams,\n    search_body: FieldCapabilityRequestBody,\n) -> Result<quickwit_proto::search::ListFieldsRequest, ElasticsearchError> {\n    let query_ast = parse_index_filter_to_query_ast(search_body.index_filter)?;\n    let query_ast_json = query_ast\n        .map(|ast| serde_json::to_string(&ast).expect(\"QueryAst should be JSON serializable\"));\n\n    Ok(quickwit_proto::search::ListFieldsRequest {\n        index_id_patterns,\n        field_patterns: search_params.fields.unwrap_or_default(),\n        start_timestamp: search_params.start_timestamp,\n        end_timestamp: search_params.end_timestamp,\n        query_ast: query_ast_json,\n        limit: None,\n    })\n}\n\n#[cfg(test)]\nmod tests {\n    use serde_json::json;\n\n    use super::*;\n\n    #[test]","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-serve/src/elasticsearch_api/model/field_capability.rs#L206-L242","documentation":"The ListFields ES-compatible API serializes the parsed index_filter QueryAst to JSON to embed it in the gRPC ListFieldsRequest. QueryAst is guaranteed to be JSON-serializable by design, so a serialization failure indicates a bug in an AST node's Serialize impl and panics via expect.","triggerScenarios":"build_list_field_request_for_es_api with an index_filter whose QueryAst serde implementation fails (non-serializable value in a custom/new AST node).","commonSituations":"Adding new QueryAst node types without deriving/implementing Serialize correctly; extremely large or malformed index_filter bodies that hit serde limits.","solutions":["Check the index_filter JSON in the request is well-formed","If you develop custom QueryAst nodes, verify their Serialize/Deserialize impls round-trip","Pin/align quickwit-query versions across the workspace"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Validate the index_filter body before the request:\nlet ast = serde_json::from_value::<serde_json::Value>(index_filter)?; // must be valid JSON\nif !index_filter.is_object() { return Err(ElasticsearchError::invalid_argument(\"index_filter\")); }","typeGuard":null,"tryCatchPattern":"// Callers of the list-fields ES API:\nmatch resp {\n    Ok(fields) => fields,\n    Err(e) if e.status() == 400 => { /* fix index_filter JSON and retry */ }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Send only well-formed boolean/term filters in _field_caps index_filter","Round-trip test custom QueryAst Serialize/Deserialize impls","Keep quickwit-query versions aligned"],"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"}