risingwavelabs/risingwave · critical

multiple arguments are not supported for non-option function

Error message

multiple arguments are not supported for non-option function

What it means

This panic comes from the `#[function]` macro's aggregate/codegen builder in src/expr/macro/src/gen.rs. When generating the state-transition expression for an aggregate whose underlying function is not written over `Option<T>` arguments, the macro only supports single-argument forms; a multi-argument (two-or-more params) non-option function hits a `todo!()` placeholder, deliberately aborting expansion. It signals an unsupported codegen case, not a runtime fault.

Source

Thrown at src/expr/macro/src/gen.rs:1007

                        quote! {{
                            let state = self.function.create_state();
                            #next_state
                        }}
                    } else {
                        quote! {{
                            let state = #state_type::default();
                            #next_state
                        }}
                    };
                    next_state = quote! {
                        match (state, v0) {
                            (Some(state), Some(v0)) => #next_state,
                            (None, Some(v0)) => #first_state,
                            (state, None) => state,
                        }
                    };
                }
                _ => todo!("multiple arguments are not supported for non-option function"),
            }
        }
        let update_state = if custom_state.is_some() {
            quote! { _ = #next_state; }
        } else {
            quote! { state = #next_state; }
        };
        let get_result = if custom_state.is_some() {
            quote! { Ok(state.downcast_ref::<#state_type>().into()) }
        } else if let AggregateFnOrImpl::Impl(impl_) = user_fn
            && impl_.finalize.is_some()
        {
            quote! {
                let state = match state.as_datum() {
                    Some(s) => s.as_scalar_ref_impl().try_into().unwrap(),
                    None => return Ok(None),
                };
                Ok(Some(self.function.finalize(state).into()))

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Rewrite the function so each argument is wrapped in `Option<T>` (the macro's supported multi-arg path), e.g. `fn my_agg(a: Option<i64>, b: Option<i64>) -> i64` and handle None cases in the body.
  2. Split the aggregate into a single-argument function, or pre-combine arguments before aggregation.
  3. If support is genuinely needed, replace the `todo!()` in gen.rs with codegen that folds over multiple `#vN` inputs, and add a test.

Example fix

// before - panics at macro expansion
#[function("my_agg(int8, int8)")]
fn my_agg(a: i64, b: i64) -> i64 { a + b }

// after - Option-wrapped args are supported
#[function("my_agg(int8, int8)")]
fn my_agg(a: Option<i64>, b: Option<i64>) -> i64 {
    match (a, b) { (Some(a), Some(b)) => a + b, _ => 0 }
}
Defensive patterns

Strategy: validation

Validate before calling

// Before declaring the aggregate, check the fn arity / Option-wrapping
fn is_macro_compatible_agg(sig: &str) -> bool {
    // only single-arg non-Option fns or Option-wrapped args are supported
    let args = sig.split('(').nth(1).unwrap_or("");
    let argc = args.split(',').filter(|a| !a.trim().is_empty()).count();
    argc == 1 || sig.contains("Option<")
}
assert!(is_macro_compatible_agg("my_agg(int8, int8)")); // panics here first, before cargo build

Prevention

When it happens

Trigger: Declaring an aggregate (e.g. via `#[aggregate]` / generate_aggregate_descriptor) whose `accumulate`-style function takes 2+ arguments and whose signature is not the `Option<T>`-argument form the macro handles (Some/None match arms). Any `#[function("agg(...) ...")]` aggregate built from a multi-arg plain (non-Option) function.

Common situations: Contributors adding a new SQL aggregate function in RisingWave write a plain multi-argument Rust function and mark it as an aggregate; older macro versions only implemented the single-arg and Option-wrapped cases, so upgrades or new function shapes hit the unimplemented branch.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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