risingwavelabs/risingwave · error

cardinality > 0

Error message

cardinality > 0

What it means

Thrown in the VALUES executor's `execute_inner` when the declared schema has zero columns. A VALUES (constant relation) must emit at least one column; a zero-column schema would make the one-row dummy chunk meaningless and downstream expression evaluation impossible.

Solutions

  1. Fix the planner so the constant relation always has at least one column (e.g. add a dummy unit column).
  2. Check the SQL that produced the plan; avoid statements yielding an empty column set.
  3. Ensure frontend and stream versions match; older frontends may emit degenerate schemas.

Example fix

-- before
SELECT;
-- after
SELECT 1; -- or ensure the VALUES clause lists at least one column
Defensive patterns

Strategy: validation

Validate before calling

// before constructing ValuesExecutor
if schema.is_empty() {
    // add a dummy unit column or reject the plan early
}

Type guard

fn has_columns(schema: &Schema) -> bool { !schema.fields.is_empty() }

Prevention

When it happens

Trigger: Creating a `ValuesExecutor` whose `schema.len() == 0`, e.g. planning a `VALUES` clause or a query that resolves to a constant relation with an empty column list.

Common situations: Frontend planner bugs producing an empty projection for a constant relation; SQL like `SELECT;` or a `VALUES` with an empty field list reaching the stream engine; misconfigured internal constant-relations used for system catalogs.

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 risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/be7d6ef0fa71481f. Report an issue: GitHub.

Appendix: source

Thrown at src/stream/src/executor/values.rs:92

        let paused_on_startup = barrier.is_pause_on_startup();

        yield Message::Barrier(barrier);

        // If it's failover, do not evaluate rows (assume they have been yielded)
        if emit {
            if paused_on_startup {
                // Wait for the data stream to be resumed before yielding the chunks.
                while let Some(barrier) = barrier_receiver.recv().await {
                    let is_resume = barrier.is_resume();
                    yield Message::Barrier(barrier);
                    if is_resume {
                        break;
                    }
                }
            }

            let cardinality = schema.len();
            ensure!(cardinality > 0);
            while !rows.is_empty() {
                // We need a one row chunk rather than an empty chunk because constant
                // expression's eval result is same size as input chunk
                // cardinality.
                let one_row_chunk = DataChunk::new_dummy(1);

                let chunk_size = DEFAULT_CHUNK_SIZE.min(rows.len());
                let mut array_builders = schema.create_array_builders(chunk_size);
                for row in rows.by_ref().take(chunk_size) {
                    for (expr, builder) in row.into_iter().zip_eq_fast(&mut array_builders) {
                        let out = expr.eval_infallible(&one_row_chunk).await;
                        builder.append_array(&out);
                    }
                }

                let columns: Vec<_> = array_builders
                    .into_iter()
                    .map(|b| b.finish().into())

View on GitHub (pinned to 6469eb736d)