risingwavelabs/risingwave · error
UDF returned a value of type {} while the declared return ty
Error message
UDF returned a value of type {} while the declared return type is {} What it means
At runtime a UDF aggregate's result array is converted against the return type recorded at UDF creation. If the external UDF server returns an array whose Arrow data type differs from the declared return type, this error is raised instead of letting a mistyped value corrupt downstream encoding.
Source
Thrown at src/expr/core/src/aggregate/user_defined.rs:94
range: Range<usize>,
) -> Result<()> {
// XXX(runji): this may be inefficient
let vis = input.visibility() & Bitmap::from_range(input.capacity(), range);
let input = input.clone_with_vis(vis);
self.update(state, &input).await
}
/// Get aggregate result from the state.
async fn get_result(&self, state: &AggregateState) -> Result<Datum> {
let state = &state.downcast_ref::<State>().0;
let arrow_output = self.runtime.call_agg_finish(state).await?;
ensure_single_row(&arrow_output, "output")?;
let output = UdfArrowConvert::default().from_array(&self.return_field, &arrow_output)?;
// The UDF runtime is external input: a server may drift from the signature it was
// checked against at creation time. Surface a mistyped result instead of letting it
// corrupt downstream value encoding.
if output.data_type() != self.return_type {
return Err(anyhow::anyhow!(
"UDF returned a value of type {} while the declared return type is {}",
output.data_type(),
self.return_type
)
.into());
}
Ok(output.datum_at(0))
}
/// Encode the state into a datum that can be stored in state table.
fn encode_state(&self, state: &AggregateState) -> Result<Datum> {
let state = &state.downcast_ref::<State>().0;
ensure_single_row(state, "state")?;
let state = UdfArrowConvert::default().from_array(&self.state_field, state)?;
Ok(state.datum_at(0))
}
/// Decode the state from a datum in state table.View on GitHub (pinned to 6469eb736d)
Solutions
- Fix the UDF server so its actual output matches its declared signature
- Recreate the UDF / aggregate in RisingWave so the declared return type matches the current server
- Add type casts in the UDF before returning results
- Verify both sides use compatible Arrow versions
Example fix
// before (UDF returns i32 while declared Int64) return arrow::array::Int32Array::from(vec![sum]); // after return arrow::array::Int64Array::from(vec![sum as i64]);
Defensive patterns
Strategy: try-catch
Validate before calling
// after receiving arrow_output, before trusting it:
if arrow_output.data_type() != declared_return_type {
// treat UDF server as drifted; fail or cast defensively
} Type guard
fn matches_declared(field: &arrow::datatypes::Field, out: &dyn arrow::array::Array) -> bool {
out.data_type() == field.data_type()
} Try / catch
match agg.get_result().await {
Ok(v) => v,
Err(e) if e.to_string().contains("declared return type") => {
// recreate UDF or cast the value; do not propagate corrupt data
}
Err(e) => return Err(e),
} Prevention
- Pin UDF server deployments and validate signature after redeploy
- Include return type checks in UDF server CI
- Use the same Arrow version on both sides
When it happens
Trigger: Calling `get_result` on a UserDefinedAggregate when the external UDF service returns a value whose Arrow DataType differs from `self.return_type` (checked via `output.data_type() != self.return_type`).
Common situations: UDF server was redeployed with a changed return type after the aggregate was created in RisingWave; the UDF implementation returns e.g. Utf8 where Int64 was declared; arrow version differences producing subtly different types.
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
- UDF returned {:?}, but expected {:?}
- column '{}' type mismatch: LanceDB type is {:?}, RisingWave
- UDF aggregate {what} has {} rows, but expected exactly 1
- UDF returned {} rows, but expected {}
- UDF returned no columns
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/18bb83b501a0d14a.
Report an issue: GitHub.