{"record":{"id":"4eeaa02f7ba3e681","repo":"risingwavelabs/risingwave","slug":"aggregate-function-is-not-supported","errorCode":null,"errorMessage":"aggregate function is not supported","messagePattern":"aggregate function is not supported","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/expr/core/src/sig/udf.rs","lineNumber":142,"sourceCode":"    Table,\n    Aggregate,\n}\n\n/// UDF implementation.\n#[async_trait::async_trait]\npub trait UdfImpl: std::fmt::Debug + Send + Sync {\n    /// Call the scalar function.\n    async fn call(&self, input: &RecordBatch) -> Result<RecordBatch>;\n\n    /// Call the table function.\n    async fn call_table_function<'a>(\n        &'a self,\n        input: &'a RecordBatch,\n    ) -> Result<BoxStream<'a, Result<RecordBatch>>>;\n\n    /// For aggregate function, create the initial state.\n    async fn call_agg_create_state(&self) -> Result<ArrayRef> {\n        bail!(\"aggregate function is not supported\");\n    }\n\n    /// For aggregate function, accumulate or retract the state.\n    async fn call_agg_accumulate_or_retract(\n        &self,\n        _state: &ArrayRef,\n        _ops: &BooleanArray,\n        _input: &RecordBatch,\n    ) -> Result<ArrayRef> {\n        bail!(\"aggregate function is not supported\");\n    }\n\n    /// For aggregate function, get aggregate result from the state.\n    async fn call_agg_finish(&self, _state: &ArrayRef) -> Result<ArrayRef> {\n        bail!(\"aggregate function is not supported\");\n    }\n\n    /// Whether the UDF talks in legacy mode.","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/expr/core/src/sig/udf.rs#L124-L160","documentation":"The default trait implementation of `call_agg_create_state` on RisingWave's UDF expression trait always fails with this message. It exists so scalar/table UDFs only need to implement aggregate hooks when they actually represent an aggregate function; calling the aggregate path on a UDF that did not override it hits this bail.","triggerScenarios":"Calling `call_agg_create_state()` on a `UdfExpr`/UDF wrapper whose underlying implementation only overrides the scalar (or table-function) evaluation methods and leaves `call_agg_create_state` as the trait default — e.g. running the aggregate streaming/batch executor over an external UDF registered as a scalar function.","commonSituations":"A developer registers a Python/SQL/external UDF intended to be used with GROUP BY aggregation but implements it as a scalar UDF; or a planner/executor bug routes an aggregate call down the UDF path without the aggregate trait methods being implemented.","solutions":["Implement `call_agg_create_state` (plus `call_agg_accumulate_or_retract` and `call_agg_finish`) for the UDF if it is meant to be an aggregate.","If the UDF is scalar-only, fix the query/planning so it is not used as an aggregate (or register it with the correct function kind).","Verify the UDF descriptor/kind in the function catalog declares `AGGREGATE` only when the aggregate trait methods are provided."],"exampleFix":"// before\nimpl UdfImpl for MyUdf {\n    async fn eval(&self, input: &RecordBatch) -> Result<ArrayRef> { /* scalar eval */ }\n}\n// after\n#[async_trait]\nimpl UdfImpl for MyUdf {\n    async fn eval(&self, input: &RecordBatch) -> Result<ArrayRef> { /* scalar eval */ }\n    async fn call_agg_create_state(&self) -> Result<ArrayRef> {\n        Ok(self.state_builder.build_initial_state())\n    }\n}","handlingStrategy":"type-guard","validationCode":"// Rust: check trait capabilities before aggregate use\nfn supports_aggregate(u: &dyn UdfImpl) -> bool { u.implements_agg() }","typeGuard":"fn is_aggregate_udf(f: &FunctionDesc) -> bool { matches!(f.kind, FunctionKind::Aggregate) }","tryCatchPattern":"match udf.call_agg_create_state().await {\n    Ok(state) => state,\n    Err(e) if e.to_string().contains(\"aggregate function is not supported\") => {\n        return Err(anyhow!(\"UDF '{}' cannot be used as an aggregate\", name));\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Only declare a function AGGREGATE in the catalog when all aggregate trait methods are implemented","Add a unit test that calls all three agg hooks for every aggregate UDF","Keep scalar and aggregate UDFs in separate registration paths"],"tags":["udf","aggregate","unsupported-operation","rust"],"backgroundTag":"method-not-implemented","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"}