{"record":{"id":"9b20bec51c33547b","repo":"quickwit-oss/quickwit","slug":"input-has-sort-fields-expected","errorCode":null,"errorMessage":"input {} has sort_fields '{}', expected '{}'","messagePattern":"input (.+?) has sort_fields '(.+?)', expected '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"quickwit/quickwit-parquet-engine/src/merge/metadata_aggregation.rs","lineNumber":89,"sourceCode":"        }\n        if input.index_uid != first.index_uid {\n            bail!(\n                \"input {} has index_uid '{}', expected '{}'\",\n                i,\n                input.index_uid,\n                first.index_uid\n            );\n        }\n        if input.partition_id != first.partition_id {\n            bail!(\n                \"input {} has partition_id {}, expected {}\",\n                i,\n                input.partition_id,\n                first.partition_id\n            );\n        }\n        if input.sort_fields != first.sort_fields {\n            bail!(\n                \"input {} has sort_fields '{}', expected '{}'\",\n                i,\n                input.sort_fields,\n                first.sort_fields\n            );\n        }\n        if input.window != first.window {\n            bail!(\n                \"input {} has window {:?}, expected {:?}\",\n                i,\n                input.window,\n                first.window\n            );\n        }\n        if !mixed_prefix_ok && input.rg_partition_prefix_len != first.rg_partition_prefix_len {\n            bail!(\n                \"input {} has rg_partition_prefix_len {}, expected {} — splits with different \\\n                 prefix lengths must not appear in the same regular merge (legacy-promotion \\","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-parquet-engine/src/merge/metadata_aggregation.rs#L71-L107","documentation":"merge_parquet_split_metadata requires all input splits to carry identical `sort_fields` (the sort schema string). This bail fires when input i's sort_fields serialization differs from inputs[0]'s. The merged output inherits one sort schema; mixing sorted orders (different fields or directions) would make the output's declared sort key meaningless and break zonemap/sorted-search assumptions.","triggerScenarios":"Calling merge_parquet_split_metadata with splits whose sort_fields differ — e.g. the index config's sort fields changed between the times different splits were created, and a compaction sweep now picks up splits from both eras.","commonSituations":"User edits sort configuration of an existing index; a schema-migration rolled out mid-lifecycle leaving old splits with the old sort key; timezone or field-name formatting differences producing string-unequal but logically similar sort fields (this function compares strings/structs exactly).","solutions":["Filter merge candidates by exact sort_fields match before grouping into merge tasks.","Force a 'sort rebuild' path (or delete/reindex) for splits written under the old sort configuration instead of compacting them with new ones.","If the mismatch is only formatting, normalize sort_fields serialization on write so equivalent schemas compare equal (see equivalent_schemas_for_compaction used by the merge engine).","Check recent index-config changes that explain which splits carry which sort_fields."],"exampleFix":"// before\nlet candidates: Vec<_> = splits.into_iter().filter(|s| s.index_uid == uid).collect();\n// after\nlet candidates: Vec<_> = splits.into_iter()\n    .filter(|s| s.index_uid == uid && s.sort_fields == expected_sort_fields)\n    .collect();","handlingStrategy":"validation","validationCode":"fn sort_fields_consistent(inputs: &[ParquetSplitMetadata]) -> bool {\n    inputs.iter().all(|s| s.sort_fields == inputs[0].sort_fields)\n}","typeGuard":"fn homogeneous_sort_fields(inputs: &[ParquetSplitMetadata]) -> Option<&str> {\n    let sf = &inputs.first()?.sort_fields;\n    inputs.iter().all(|s| &s.sort_fields == sf).then_some(sf.as_str())\n}","tryCatchPattern":"match merge_parquet_split_metadata(&inputs, &output, mixed) {\n    Err(e) if e.to_string().contains(\"sort_fields\") => {\n        warn!(\"sort-schema drift in merge batch: {e:#}; scheduling rebuild for old splits\");\n        schedule_sort_rebuild(&inputs);\n    }\n    other => other?,\n}","preventionTips":["Filter candidates by exact sort_fields match before grouping.","Treat index sort-config changes as requiring a rebuild/migration of existing splits.","Normalize sort_fields serialization at write time to avoid cosmetic mismatches."],"tags":["rust","merge","sort-schema","invariant-violation"],"backgroundTag":"schema-validation-failed","analyzedSha":"a39730c5cdcd1a4fe798403737ae293999ea21f8","analyzedAt":"2026-09-08T13:19:37.784Z","contentChangedAt":"2026-09-08T13:19:37.784Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}