quickwit-oss/quickwit · error
attempted to merge zero splits
Error message
attempted to merge zero splits
What it means
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.
Source
Thrown at quickwit/quickwit-indexing/src/actors/merge_executor.rs:293
merge_split_id: SplitId,
splits: &[SplitMetadata],
) -> anyhow::Result<SplitAttrs> {
let partition_id = combine_partition_ids_aux(splits.iter().map(|split| split.partition_id));
let time_range: Option<RangeInclusive<DateTime>> = merge_time_range(splits);
let uncompressed_docs_size_in_bytes = sum_doc_sizes_in_bytes(splits);
let num_docs = sum_num_docs(splits);
let replaced_split_ids: Vec<SplitId> = splits
.iter()
.map(|split| split.split_id().clone())
.collect();
let delete_opstamp = splits
.iter()
.map(|split| split.delete_opstamp)
.min()
.unwrap_or(0);
let doc_mapping_uid = splits
.first()
.ok_or_else(|| anyhow::anyhow!("attempted to merge zero splits"))?
.doc_mapping_uid;
if splits
.iter()
.any(|split| split.doc_mapping_uid != doc_mapping_uid)
{
anyhow::bail!("attempted to merge splits with different doc mapping uid");
}
Ok(SplitAttrs {
node_id: pipeline_id.node_id.clone(),
index_uid: pipeline_id.index_uid.clone(),
source_id: pipeline_id.source_id.clone(),
doc_mapping_uid,
split_id: merge_split_id,
partition_id,
replaced_split_ids,
time_range,
num_docs,
uncompressed_docs_size_in_bytes,View on GitHub (pinned to a39730c5cd)
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.
Defensive patterns
Strategy: try-catch
Try / catch
match merge_executor.merge(task) {
Err(e) if e.to_string().contains("attempted to merge zero splits") => {
// log and drop the empty merge task; investigate planner
}
r => r?,
} Prevention
- 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.
When it happens
Trigger: `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.
Common situations: Race between merge planning and split deletion, corrupted merge task payloads, or custom/test code calling `fake_merge` with no splits.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- doc mapper must be present if there are delete tasks
- attempted to merge splits with different doc mapping uid
- execute_merge_operation: sources.len() ({}) != op.splits.len
- position of a Kinesis shard should never be EOF
- timestamp_secs must be UInt64 or Int64 for MC-3 check
AI-assisted analysis of quickwit-oss/quickwit@a39730c5cd (2026-09-08).
Data as JSON: /api/errors/7b9190fe35794f54.
Report an issue: GitHub.