quickwit-oss/quickwit · error

merge_parquet_split_metadata requires at least one input spl

Error message

merge_parquet_split_metadata requires at least one input split

What it means

merge_parquet_split_metadata aggregates Parquet split metadata for a merge and requires at least one input split; it indexes inputs[0] to establish baseline invariant fields, so an empty input slice is rejected up front.

Source

Thrown at quickwit/quickwit-parquet-engine/src/merge/metadata_aggregation.rs:57

/// agree due to compaction scope grouping / MP-3).
///
/// # Preconditions
///
/// All input splits must share the same kind, index_uid, partition_id,
/// sort_fields, and window. In the default case (`mixed_prefix_ok = false`)
/// they must also share `rg_partition_prefix_len`. In legacy-promotion
/// mode (`mixed_prefix_ok = true`) the prefix-len equality check is
/// skipped because inputs come from different prefix buckets — the
/// output's prefix_len is taken from the writer's KV stamp via
/// `output.output_rg_partition_prefix_len` (CS-1), so the input-side
/// equality is no longer load-bearing for the metastore record.
pub fn merge_parquet_split_metadata(
    inputs: &[ParquetSplitMetadata],
    output: &MergeOutputFile,
    mixed_prefix_ok: bool,
) -> Result<ParquetSplitMetadata> {
    if inputs.is_empty() {
        bail!("merge_parquet_split_metadata requires at least one input split");
    }

    let first = &inputs[0];

    // Validate invariant fields: all inputs must agree on these.
    for (i, input) in inputs.iter().enumerate().skip(1) {
        if input.kind != first.kind {
            bail!(
                "input {} has kind {:?}, expected {:?}",
                i,
                input.kind,
                first.kind
            );
        }
        if input.index_uid != first.index_uid {
            bail!(
                "input {} has index_uid '{}', expected '{}'",
                i,

View on GitHub (pinned to a39730c5cd)

Solutions

  1. Ensure the merge caller checks for non-empty input and skips the merge instead of invoking it
  2. Fix upstream split selection so empty merge tasks are never scheduled
  3. Add a guard in handle: if inputs.is_empty() { return early/log } before calling the function

Example fix

// before
merge_parquet_split_metadata(&splits, &output, false)?;
// after
if splits.is_empty() {
    return Ok(None); // nothing to merge
}
Some(merge_parquet_split_metadata(&splits, &output, false)?)
Defensive patterns

Strategy: try-catch

Validate before calling

if inputs.is_empty() {
    // skip merge entirely
    return Ok(None);
}

Type guard

fn mergeable(inputs: &[ParquetSplitMetadata]) -> bool { !inputs.is_empty() }

Try / catch

match merge_parquet_split_metadata(&inputs, &output, false) {
    Err(e) if e.to_string().contains("at least one input split") => {
        warn!("empty merge skipped: {e}");
        Ok(None)
    }
    other => other.map(Some),
}

Prevention

When it happens

Trigger: Calling merge_parquet_split_metadata (directly or via the merge handle) with an empty inputs slice, e.g. scheduling a merge after all candidate splits were filtered out.

Common situations: A merge task computed against splits that were deleted/consumed before execution; upstream filtering logic removing all inputs; a unit-test edge case.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of quickwit-oss/quickwit@a39730c5cd (2026-09-08). Data as JSON: /api/errors/53696e0790e6e97b. Report an issue: GitHub.