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
- Log `rhs_chunk.cardinality()` and file an issue with the pipeline definition
- Recreate the streaming job to reset executor state
- Upgrade RisingWave for possible row_merge fixes
- 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
- Ensure upstream right-side chunks are split to 1–2 rows before merging
- Keep custom executor/test wiring consistent with the 1–2 row contract
- Watch release notes for chunking regressions affecting row_merge
- Recreate affected jobs to clear bad buffered chunks
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
- lhs and rhs chunk cardinality should be the same
- lhs buffer should not be empty
- lhs chunk cardinality should be 1 or 2
- rhs buffer should not be empty
- iceberg pk-index writer
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)