risingwavelabs/risingwave · error
lhs chunk cardinality should be 1 or 2
Error message
lhs chunk cardinality should be 1 or 2
What it means
`build_chunk` in the row-merge executor pairs one lhs row with one rhs row, so each chunk must have cardinality 1 or 2 (the executor's designed batching granularity). A lhs chunk with 0 or more than 2 rows violates the executor's batching contract and bails with this error.
Solutions
- Capture the actual lhs chunk cardinality (log `lhs_chunk.cardinality()`) and file an issue — this reflects broken internal batching invariants
- Recreate the streaming job as a workaround
- Check the RisingWave version for known row_merge batching regressions and upgrade
- When writing tests against row_merge, ensure test chunks are split to cardinality 1–2 before calling build_chunk
Example fix
// before (test wiring calling build_chunk directly with a big chunk)
Message::Chunk(big_chunk)
// after
for small_chunk in split_chunk(big_chunk, 2) { Message::Chunk(small_chunk) } Defensive patterns
Strategy: try-catch
Try / catch
match build_result {
Err(e) if e.to_string().contains("lhs chunk cardinality should be 1 or 2") => {
// Batching invariant broken upstream: file an issue and recreate the job
report_issue_with_chunk_trace();
recreate_streaming_job(job_id);
}
other => other?,
} Prevention
- When wiring or testing executors that call build_chunk, split chunks to at most 2 rows first
- Add assertions in custom pipelines that chunks entering row_merge are pre-split
- Follow upstream chunking regressions in release notes before upgrading
- Recreate jobs that previously logged cardinality anomalies
When it happens
Trigger: Internal: chunks delivered to `build_chunk` contain more than 2 rows (or are empty), meaning upstream buffering failed to split chunks to the expected 1–2 row size before merging.
Common situations: Changes in upstream chunking behavior feeding row_merge; bugs where a whole input chunk bypasses the split logic; misuse in tests/custom executor wiring.
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
- rhs buffer should not be empty
- rhs chunk cardinality should be 1 or 2
- iceberg pk-index writer
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/3460ab101f086b17.
Report an issue: GitHub.
Appendix: source
Thrown at src/stream/src/executor/row_merge.rs:150
data_types,
lhs_mapping,
rhs_mapping,
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)