risingwavelabs/risingwave · error

data type must be DataType::List

Error message

data type must be DataType::List

What it means

For functions returning `impl Iterator` with ListWrite output, the generated code needs the return type to be `DataType::List` to create an element array builder. If `return_type` is not a List, the generated code panics with `data type must be DataType::List` at runtime.

Source

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

        };
        // the output expression in `eval_row`
        let row_output = match user_fn.writer_type_kind {
            Some(WriterTypeKind::FmtWrite) => quote! {{
                let mut writer = String::new();
                #output.map(|_| writer.into())
            }},
            Some(WriterTypeKind::IoWrite) => quote! {{
                let mut writer = Vec::new();
                #output.map(|_| writer.into())
            }},
            Some(WriterTypeKind::JsonbbBuilder) => quote! {{
                let mut writer = jsonbb::Builder::<Vec<u8>>::new();
                #output.map(|_| JsonbVal::from(writer.finish()).into())
            }},
            Some(WriterTypeKind::ListWrite) => quote! {{
                let mut writer = {
                    let DataType::List(list_ty) = &self.context.return_type else {
                        panic!("data type must be DataType::List");
                    };
                    list_ty.elem().create_array_builder(1)
                };
                #output.map(|_| ListValue::new(writer.finish()).into())
            }},
            None if user_fn.core_return_type == "impl AsRef < [u8] >" => quote! {
                #output.map(|s| s.as_ref().into())
            },
            None => quote! {{
                let output #annotation = #output;
                output.map(|s| s.into())
            }},
        };
        // the main body in `eval`
        let eval = if let Some(batch_fn) = &self.batch_fn {
            assert!(
                !variadic,
                "customized batch function is not supported for variadic functions"

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Fix the type_infer function to always return DataType::List for this function
  2. Fix the frontend inference so the return type is a List
  3. Guard the generated code with a clear error instead of panic if the type can vary

Example fix

// before
type_infer = "|_| Ok(DataType::Varchar)"
// after
type_infer = "|_| Ok(DataType::List(Box::new(DataType::Varchar.into())))"
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_list_return(rt: &DataType) -> Result<(), String> {
    match rt { DataType::List(_) => Ok(()), other => Err(format!("return type must be List, got {other:?}")) }
}

Type guard

fn as_list_type(rt: &DataType) -> Option<&ListType> { match rt { DataType::List(l) => Some(l), _ => None } }

Try / catch

match as_list_type(&func.return_type()) {
    None => return Err("function must return a list".into()),
    Some(list_ty) => run_list_write_udf(list_ty),
}

Prevention

When it happens

Trigger: A scalar function declared with `#[return_type = "list"]`/ListWrite writer whose actual inferred return type is not a List — e.g. type inference returns a scalar or struct type for a function generating list output.

Common situations: Custom type_infer returning the wrong type for a list-producing function; frontend infers a non-list return for a UDF that uses the ListWrite writer path.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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