{"record":{"id":"9ed014197a8fa066","repo":"quickwit-oss/quickwit","slug":"input-has-index-uid-expected","errorCode":null,"errorMessage":"input {} has index_uid '{}', expected '{}'","messagePattern":"input (.+?) has index_uid '(.+?)', expected '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"quickwit/quickwit-parquet-engine/src/merge/metadata_aggregation.rs","lineNumber":73,"sourceCode":") -> Result<ParquetSplitMetadata> {\n    if inputs.is_empty() {\n        bail!(\"merge_parquet_split_metadata requires at least one input split\");\n    }\n\n    let first = &inputs[0];\n\n    // Validate invariant fields: all inputs must agree on these.\n    for (i, input) in inputs.iter().enumerate().skip(1) {\n        if input.kind != first.kind {\n            bail!(\n                \"input {} has kind {:?}, expected {:?}\",\n                i,\n                input.kind,\n                first.kind\n            );\n        }\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,","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-parquet-engine/src/merge/metadata_aggregation.rs#L55-L91","documentation":"merge_parquet_split_metadata enforces that all input splits belong to the same index. This bail fires when input i's `index_uid` differs from `inputs[0].index_uid`. A merge can only produce output metadata for one index, so cross-index inputs indicate a broken compaction-scope selection.","triggerScenarios":"Calling merge_parquet_split_metadata with inputs whose ParquetSplitMetadata.index_uid values differ — e.g. the merge planner grouped tasks by partition_id but not index_uid, or split metadata was deserialized from the wrong index task queue.","commonSituations":"Control-plane bug assigning splits from two indexes to one indexing task; index deletion/re-creation reusing a partition id while stale splits from the old index remain; test fixtures with copy-pasted index_uids.","solutions":["Fix the merge task grouping to include index_uid in the scope key so only same-index splits are merged together.","Check for stale splits from a deleted/recreated index and purge them from the merge candidate set.","Verify the split metadata source (task queue / metastore fetch) filters by the current index_uid.","In tests, use a constructor helper that stamps one shared index_uid on every input."],"exampleFix":"// before\ncandidates.retain(|s| s.partition_id == task.partition_id);\n// after\ncandidates.retain(|s| s.index_uid == task.index_uid && s.partition_id == task.partition_id);","handlingStrategy":"validation","validationCode":"fn single_index(inputs: &[ParquetSplitMetadata]) -> bool {\n    inputs.iter().all(|s| s.index_uid == inputs[0].index_uid)\n}","typeGuard":"fn same_index_uid(inputs: &[ParquetSplitMetadata]) -> Option<&str> {\n    let uid = &inputs.first()?.index_uid;\n    inputs.iter().all(|s| &s.index_uid == uid).then_some(uid.as_str())\n}","tryCatchPattern":"match merge_parquet_split_metadata(&inputs, &output, mixed) {\n    Err(e) if e.to_string().contains(\"index_uid\") => {\n        error!(\"cross-index merge task detected: {e:#}; dropping task and alerting\");\n        // quarantine the task, do not retry as-is\n    }\n    other => other?,\n}","preventionTips":["Always filter merge candidates by index_uid at the metastore query level.","Purge stale splits when an index is deleted and recreated.","Include index_uid in task IDs / queue routing so cross-index batches can't form."],"tags":["rust","merge","index-uid","invariant-violation"],"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-14T11:17:12.474Z"}