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

  1. Fix the UDF server so its actual output matches its declared signature
  2. Recreate the UDF / aggregate in RisingWave so the declared return type matches the current server
  3. Add type casts in the UDF before returning results
  4. 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

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


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