risingwavelabs/risingwave · error

rhs chunk cardinality should be 1 or 2

Error message

rhs chunk cardinality should be 1 or 2

What it means

The rhs counterpart of the lhs cardinality check in `build_chunk`: rhs chunks must contain exactly 1 or 2 rows so they can be merged row-by-row with lhs chunks of the same cardinality. Any other size breaks the pairwise merge contract.

Solutions

  1. Log `rhs_chunk.cardinality()` and file an issue with the pipeline definition
  2. Recreate the streaming job to reset executor state
  3. Upgrade RisingWave for possible row_merge fixes
  4. In tests, split rhs chunks to cardinality 1–2 before calling build_chunk

Example fix

// before
Message::Chunk(rhs_big_chunk)
// after
for c in split_chunk(rhs_big_chunk, 2) { Message::Chunk(c) }
Defensive patterns

Strategy: try-catch

Try / catch

match build_result {
    Err(e) if e.to_string().contains("rhs chunk cardinality should be 1 or 2") => {
        // Right-side chunking invariant broken: report and recreate
        report_issue_with_chunk_trace();
        recreate_streaming_job(job_id);
    }
    other => other?,
}

Prevention

When it happens

Trigger: Internal: rhs chunk with cardinality 0 or >2 reaches `build_chunk` because upstream chunk splitting on the right side did not enforce the 1–2 row size.

Common situations: Same as lhs case: upstream chunking regressions, bypassed split logic, or direct invocations in tests.

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


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/3d424bec85a6cc05. Report an issue: GitHub.

Appendix: source

Thrown at src/stream/src/executor/row_merge.rs:153

                    lhs_chunk.clone(),
                    rhs_chunk,
                )?;
            }
        }
    }

    fn build_chunk(
        data_types: &[DataType],
        lhs_mapping: &ColIndexMapping,
        rhs_mapping: &ColIndexMapping,
        lhs_chunk: StreamChunk,
        rhs_chunk: StreamChunk,
    ) -> Result<Message, StreamExecutorError> {
        if !(1..=2).contains(&lhs_chunk.cardinality()) {
            bail!("lhs chunk cardinality should be 1 or 2");
        }
        if !(1..=2).contains(&rhs_chunk.cardinality()) {
            bail!("rhs chunk cardinality should be 1 or 2");
        }
        if lhs_chunk.cardinality() != rhs_chunk.cardinality() {
            bail!("lhs and rhs chunk cardinality should be the same");
        }
        let cardinality = lhs_chunk.cardinality();
        let mut ops = Vec::with_capacity(cardinality);
        let mut merged_rows = vec![vec![Datum::None; data_types.len()]; cardinality];
        for (i, (op, lhs_row)) in lhs_chunk.rows().enumerate() {
            ops.push(op);
            for (j, d) in lhs_row.iter().enumerate() {
                // NOTE(kwannoel): Unnecessary columns will not have a mapping,
                // for instance extra row count column.
                // those can be skipped here.
                if let Some(out_index) = lhs_mapping.try_map(j) {
                    merged_rows[i][out_index] = d.to_owned_datum();
                }
            }
        }

View on GitHub (pinned to 6469eb736d)