{"record":{"id":"7b9190fe35794f54","repo":"quickwit-oss/quickwit","slug":"attempted-to-merge-zero-splits","errorCode":null,"errorMessage":"attempted to merge zero splits","messagePattern":"attempted to merge zero splits","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"quickwit/quickwit-indexing/src/actors/merge_executor.rs","lineNumber":293,"sourceCode":"    merge_split_id: SplitId,\n    splits: &[SplitMetadata],\n) -> anyhow::Result<SplitAttrs> {\n    let partition_id = combine_partition_ids_aux(splits.iter().map(|split| split.partition_id));\n    let time_range: Option<RangeInclusive<DateTime>> = merge_time_range(splits);\n    let uncompressed_docs_size_in_bytes = sum_doc_sizes_in_bytes(splits);\n    let num_docs = sum_num_docs(splits);\n    let replaced_split_ids: Vec<SplitId> = splits\n        .iter()\n        .map(|split| split.split_id().clone())\n        .collect();\n    let delete_opstamp = splits\n        .iter()\n        .map(|split| split.delete_opstamp)\n        .min()\n        .unwrap_or(0);\n    let doc_mapping_uid = splits\n        .first()\n        .ok_or_else(|| anyhow::anyhow!(\"attempted to merge zero splits\"))?\n        .doc_mapping_uid;\n    if splits\n        .iter()\n        .any(|split| split.doc_mapping_uid != doc_mapping_uid)\n    {\n        anyhow::bail!(\"attempted to merge splits with different doc mapping uid\");\n    }\n    Ok(SplitAttrs {\n        node_id: pipeline_id.node_id.clone(),\n        index_uid: pipeline_id.index_uid.clone(),\n        source_id: pipeline_id.source_id.clone(),\n        doc_mapping_uid,\n        split_id: merge_split_id,\n        partition_id,\n        replaced_split_ids,\n        time_range,\n        num_docs,\n        uncompressed_docs_size_in_bytes,","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-indexing/src/actors/merge_executor.rs#L275-L311","documentation":"Merge execution computes split attributes (doc mapping uid, delete opstamp, etc.) from the list of splits to merge. An empty split list is an invariant violation — merges are only supposed to be scheduled with at least two splits — so `merge_split_attrs` fails on `splits.first()` with this error. It indicates a bug in merge scheduling rather than user input.","triggerScenarios":"`process_merge` or `fake_merge` in the merge actor invokes `merge_split_attrs` with an empty `Vec<Split>` — i.e. the merge planner produced an empty merge task or the splits list was drained before execution.","commonSituations":"Race between merge planning and split deletion, corrupted merge task payloads, or custom/test code calling `fake_merge` with no splits.","solutions":["Investigate the merge planner/scheduler that emitted an empty merge task; this is an internal invariant violation.","Check the indexing pipeline logs for the task that produced the empty split list.","Filter out empty merge tasks before dispatching to the merge actor.","If reproducible, report it as a Quickwit bug with the offending merge task."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match merge_executor.merge(task) {\n    Err(e) if e.to_string().contains(\"attempted to merge zero splits\") => {\n        // log and drop the empty merge task; investigate planner\n    }\n    r => r?,\n}","preventionTips":["Filter empty merge tasks before dispatching to the merge actor.","Monitor for races between merge planning and split deletion.","Treat occurrences as bugs and capture the full task for reporting."],"tags":["indexing","merge","invariant"],"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"}