risingwavelabs/risingwave · error

children expression must not be empty for constant lookup…

Error message

children expression must not be empty for constant lookup expression

What it means

`build_constant_lookup_expr` builds a CASE-like lookup expression whose first child must be the operand compared against constant WHEN keys. With zero children there is no operand, so construction is impossible and the builder rejects it up front.

Solutions

  1. Ensure the expression always passes at least the operand child; check the planner rewrite that creates constant_lookup.
  2. Update/fix the frontend rule that collapsed the CASE incorrectly.
  3. If hand-building expressions, include operand + (when, then)+ pairs.
Defensive patterns

Strategy: validation

Validate before calling

// before building
if children.is_empty() { return Err(anyhow!("constant_lookup requires an operand child")); }

Prevention

When it happens

Trigger: Invoking the internal `constant_lookup(...)` function (used by the planner for constant-key CASE) with an empty children vector, typically from a planner bug or hand-constructed expression.

Common situations: Frontend/planner mis-optimization producing an empty argument list; custom tooling building expression protobufs manually.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at src/expr/impl/src/scalar/case.rs:268

impl<E: AsyncExpression> AsyncExpression for ConstantLookupExpression<E> {
    async fn eval(&self, input: &DataChunk) -> Result<ArrayRef> {
        eval_constant_lookup!(async, self, input)
    }

    async fn eval_row(&self, input: &OwnedRow) -> Result<Datum> {
        let datum = self.operand.eval_row(input).await?;
        lookup!(async, self, datum, input)
    }
}

#[build_function("constant_lookup(...) -> any", type_infer = "unreachable")]
fn build_constant_lookup_expr(
    return_type: DataType,
    children: Vec<BoxedExpression>,
) -> Result<BoxedExpression> {
    if children.is_empty() {
        bail!("children expression must not be empty for constant lookup expression");
    }

    let mut children = children;

    let operand = children.remove(0);

    let mut arms = HashMap::new();

    // Build the `arms` with iterating over `when` & `then` clauses
    let mut iter = children.into_iter().array_chunks();
    for [when, then] in iter.by_ref() {
        let Ok(Some(s)) = when.eval_const() else {
            bail!("expect when expression to be const");
        };
        arms.insert(s, then);
    }

    let fallback = if let Some(else_clause) = iter.into_remainder().next() {

View on GitHub (pinned to 6469eb736d)