{"record":{"id":"8fdbf4874a3ebe8f","repo":"quickwit-oss/quickwit","slug":"input-has-kind-expected","errorCode":null,"errorMessage":"input {} has kind {:?}, expected {:?}","messagePattern":"input (.+?) has kind (.+?), expected (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"quickwit/quickwit-parquet-engine/src/merge/metadata_aggregation.rs","lineNumber":65,"sourceCode":"/// skipped because inputs come from different prefix buckets — the\n/// output's prefix_len is taken from the writer's KV stamp via\n/// `output.output_rg_partition_prefix_len` (CS-1), so the input-side\n/// equality is no longer load-bearing for the metastore record.\npub fn merge_parquet_split_metadata(\n    inputs: &[ParquetSplitMetadata],\n    output: &MergeOutputFile,\n    mixed_prefix_ok: bool,\n) -> 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,","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-parquet-engine/src/merge/metadata_aggregation.rs#L47-L83","documentation":"merge_parquet_split_metadata validates that every input ParquetSplitMetadata beyond the first agrees with the first input on invariant fields. This bail fires when an input's `kind` (split kind enum) differs from `inputs[0].kind`. The invariant exists because compaction groups splits by scope (MP-3): a merged output can only inherit one kind, so mixing kinds would produce an ill-defined metastore record.","triggerScenarios":"Calling merge_parquet_split_metadata(inputs, output, mixed_prefix_ok) with a slice whose first element has, say, kind Kind::Created, but a later element has Kind::Merged or another variant. Typically caused by a bug in the merge-scope grouping logic (e.g. grouping by index/partition but forgetting kind), or by test fixtures hand-constructing split metadata with inconsistent kinds.","commonSituations":"A merge planner bug selects splits across kinds; a new split kind was added and the grouping key in ParquetMergeExecutor wasn't updated; hand-written test inputs where the first split's kind differs from the rest; metastore records written by an older version whose kind field changed.","solutions":["Fix the upstream grouping so all splits selected for one merge share the same `kind` (include kind in the merge-scope/grouping key in ParquetMergeExecutor).","Log which splits were selected for the merge and compare their kind values to locate the off-by-one or stale-selection source.","In tests, build all input ParquetSplitMetadata with a shared helper that sets a single consistent kind.","If kinds legitimately differ, run them as separate merge operations instead of one."],"exampleFix":"// before\nlet inputs = vec![created_split, merged_split];\nmerge_parquet_split_metadata(&inputs, &output, false)?;\n// after\nlet inputs = vec![created_split, created_split_2]; // same kind per merge\nmerge_parquet_split_metadata(&inputs, &output, false)?;","handlingStrategy":"validation","validationCode":"fn kinds_consistent(inputs: &[ParquetSplitMetadata]) -> bool {\n    inputs.iter().all(|s| s.kind == inputs[0].kind)\n}\nif !kinds_consistent(&inputs) { /* skip or split the merge task */ }","typeGuard":"fn all_same_kind(inputs: &[ParquetSplitMetadata]) -> Option<Kind> {\n    let first = inputs.first()?.kind;\n    inputs.iter().all(|s| s.kind == first).then_some(first)\n}","tryCatchPattern":"match merge_parquet_split_metadata(&inputs, &output, mixed_prefix_ok) {\n    Err(e) if e.to_string().contains(\"has kind\") => {\n        warn!(\"mixed-kind merge task rejected: {e:#}; splitting task\");\n        // re-group inputs by kind and retry each group\n    }\n    other => other?,\n}","preventionTips":["Include `kind` in the merge-scope grouping key alongside index_uid and partition_id.","Build test split metadata through one shared helper so kinds can't drift.","Add a debug assertion on the merge task right after planning."],"tags":["rust","merge","metadata","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"}