{"record":{"id":"4ea7fb40567ba6f5","repo":"quickwit-oss/quickwit","slug":"internal-error-got-sort-value2-but-no-sort-extra","errorCode":null,"errorMessage":"Internal error: Got sort_value2, but no sort extractor","messagePattern":"Internal error: Got sort_value2, but no sort extractor","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"quickwit/quickwit-search/src/top_k_collector.rs","lineNumber":859,"sourceCode":"                .first\n                .convert_to_u64_ff_val(search_after_sort_value, sort_order1)\n            {\n                sort_value = Some(new_value);\n            } else {\n                // Value is out of bounds, we ignore sort_value2 and disable the whole\n                // search_after\n                return None;\n            }\n        }\n        let mut sort_value2 = None;\n        if let Some(search_after_sort_value) = search_after\n            .sort_value2\n            .and_then(|sort_value2| sort_value2.sort_value)\n        {\n            let extractor = score_extractor\n                .second\n                .as_ref()\n                .expect(\"Internal error: Got sort_value2, but no sort extractor\");\n            if let Some(new_value) =\n                extractor.convert_to_u64_ff_val(search_after_sort_value, sort_order2)\n            {\n                sort_value2 = Some(new_value);\n            }\n        }\n        Some(Self {\n            sort_value,\n            sort_value2,\n            compare_on_equal: !search_after.split_id.is_empty(),\n            doc_id: search_after.doc_id,\n        })\n    }\n}\n","sourceCodeStart":841,"sourceCodeEnd":874,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-search/src/top_k_collector.rs#L841-L874","documentation":"When a leaf search response contains a second sort value (sort_value2) for search_after handling, the collector requires the corresponding second sort extractor (score_extractor.second) to exist. A response carrying sort_value2 without its extractor indicates the root and leaf disagree on the sort configuration, so the code panics as an internal error.","triggerScenarios":"A query with two sort fields where the leaf built sort values but the extractor tuple's second element is None; typically mismatched sort field resolution between root and leaf (e.g. sort field exists on leaf but wasn't set as extractor at root, or a tantivy/quickwit version skew).","commonSituations":"Elasticsearch-compatible search_after requests with two-level sorting; rolling upgrades where nodes run different quickwit versions and serialize sort metadata differently.","solutions":["Verify all cluster nodes run the same quickwit version","Confirm the search request's second sort field resolves to a valid extractor (not score/mixed incorrectly)","Reproduce with the exact query JSON and check sort field parsing in the root handler","If it persists, capture the query and open a bug with the reproduction"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before issuing a two-sort search_after query, confirm both sort fields exist in the index mapping:\nGET /_fields — verify \"field1\" and \"field2\" are indexed and sortable.","typeGuard":"fn has_second_extractor(hit: &PartialHit) -> bool {\n    hit.sort_value2.is_some() // only build search_after when leaf returned extractor-consistent values\n}","tryCatchPattern":null,"preventionTips":["Keep all cluster nodes on the same quickwit version","Test two-level sort + search_after queries against the target index schema","Avoid mixing score and field sorts inconsistently"],"tags":["rust","search","sorting","panic","search-after"],"backgroundTag":"internal-invariant-violation","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"}