{"record":{"id":"18bb83b501a0d14a","repo":"risingwavelabs/risingwave","slug":"udf-returned-a-value-of-type-while-the-declared","errorCode":null,"errorMessage":"UDF returned a value of type {} while the declared return type is {}","messagePattern":"UDF returned a value of type (.+?) while the declared return type is (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/expr/core/src/aggregate/user_defined.rs","lineNumber":94,"sourceCode":"        range: Range<usize>,\n    ) -> Result<()> {\n        // XXX(runji): this may be inefficient\n        let vis = input.visibility() & Bitmap::from_range(input.capacity(), range);\n        let input = input.clone_with_vis(vis);\n        self.update(state, &input).await\n    }\n\n    /// Get aggregate result from the state.\n    async fn get_result(&self, state: &AggregateState) -> Result<Datum> {\n        let state = &state.downcast_ref::<State>().0;\n        let arrow_output = self.runtime.call_agg_finish(state).await?;\n        ensure_single_row(&arrow_output, \"output\")?;\n        let output = UdfArrowConvert::default().from_array(&self.return_field, &arrow_output)?;\n        // The UDF runtime is external input: a server may drift from the signature it was\n        // checked against at creation time. Surface a mistyped result instead of letting it\n        // corrupt downstream value encoding.\n        if output.data_type() != self.return_type {\n            return Err(anyhow::anyhow!(\n                \"UDF returned a value of type {} while the declared return type is {}\",\n                output.data_type(),\n                self.return_type\n            )\n            .into());\n        }\n        Ok(output.datum_at(0))\n    }\n\n    /// Encode the state into a datum that can be stored in state table.\n    fn encode_state(&self, state: &AggregateState) -> Result<Datum> {\n        let state = &state.downcast_ref::<State>().0;\n        ensure_single_row(state, \"state\")?;\n        let state = UdfArrowConvert::default().from_array(&self.state_field, state)?;\n        Ok(state.datum_at(0))\n    }\n\n    /// Decode the state from a datum in state table.","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/expr/core/src/aggregate/user_defined.rs#L76-L112","documentation":"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.","triggerScenarios":"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`).","commonSituations":"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.","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"],"exampleFix":"// before (UDF returns i32 while declared Int64)\nreturn arrow::array::Int32Array::from(vec![sum]);\n// after\nreturn arrow::array::Int64Array::from(vec![sum as i64]);","handlingStrategy":"try-catch","validationCode":"// after receiving arrow_output, before trusting it:\nif arrow_output.data_type() != declared_return_type {\n    // treat UDF server as drifted; fail or cast defensively\n}","typeGuard":"fn matches_declared(field: &arrow::datatypes::Field, out: &dyn arrow::array::Array) -> bool {\n    out.data_type() == field.data_type()\n}","tryCatchPattern":"match agg.get_result().await {\n    Ok(v) => v,\n    Err(e) if e.to_string().contains(\"declared return type\") => {\n        // recreate UDF or cast the value; do not propagate corrupt data\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["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"],"tags":["udf","arrow","type-mismatch","rust"],"backgroundTag":"type-mismatch","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}