{"record":{"id":"034a4041bbb9dc5b","repo":"apache/cassandra","slug":"final-function-s-isn-t-a-scalar-function","errorCode":null,"errorMessage":"Final function %s isn't a scalar function","messagePattern":"Final function (.+?) isn't a scalar function","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/schema/CreateAggregateStatement.java","lineNumber":163,"sourceCode":"            throw ire(\"State function %s return type must be the same as the first argument type - check STYPE, argument and return types\",\n                      stateFunctionString());\n        }\n\n        /*\n         * Resolve the final function and return type\n         */\n\n        UserFunction finalFunction = null;\n        AbstractType<?> returnType = stateFunction.returnType();\n\n        if (null != finalFunctionName)\n        {\n            finalFunction = keyspace.userFunctions.find(finalFunctionName, singletonList(stateType)).orElse(null);\n            if (null == finalFunction)\n                throw ire(\"Final function %s doesn't exist\", finalFunctionString());\n\n            if (finalFunction.isAggregate())\n                throw ire(\"Final function %s isn't a scalar function\", finalFunctionString());\n\n            // override return type with that of the final function\n            returnType = finalFunction.returnType();\n        }\n\n        /*\n         * Validate initial condition\n         */\n\n        ByteBuffer initialValue = null;\n        if (null != rawInitialValue)\n        {\n            String term = rawInitialValue.toString();\n            initialValue = Term.asBytes(keyspaceName, term, stateType);\n\n            if (null != initialValue)\n            {\n                try","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/schema/CreateAggregateStatement.java#L145-L181","documentation":"Thrown when the function named as FINALFUNC exists but is itself an aggregate (UDA) rather than a scalar function (UDF). A final function must be a plain scalar function transforming the state value; nesting aggregates is not allowed.","triggerScenarios":"CREATE AGGREGATE ... FINALFUNC <name> where <name> resolves (with one argument of the state type) to an existing CREATE AGGREGATE definition instead of a CREATE FUNCTION definition.","commonSituations":"Name collision where an aggregate and function share a name; copy-pasting an aggregate name into the FINALFUNC slot by mistake.","solutions":["Point FINALFUNC at a scalar function created with CREATE FUNCTION, not an aggregate.","Rename the colliding aggregate/function so the names are distinct.","Drop the FINALFUNC clause if no final transformation is needed."],"exampleFix":"// before\nCREATE AGGREGATE ks.a(int) SFUNC s STYPE int FINALFUNC other_aggregate;\n// after\nCREATE AGGREGATE ks.a(int) SFUNC s STYPE int FINALFUNC scalar_final;","handlingStrategy":"validation","validationCode":"var candidate = keyspace.userFunctions.find(finalFuncName, List.of(stateType));\nif (candidate.isPresent() && candidate.get().isAggregate())\n    throw new IllegalStateException(finalFuncName + \" is an aggregate, not a scalar function\");","typeGuard":null,"tryCatchPattern":"try { session.execute(ddl); }\ncatch (InvalidRequestException e) {\n    if (e.getMessage().contains(\"isn't a scalar function\")) { /* use a UDF for FINALFUNC */ }\n    else throw e;\n}","preventionTips":["Never give an aggregate and a function the same name and signature.","Review FINALFUNC references against system_schema.functions where is_aggregate is false."],"tags":["cql","user-defined-function","schema"],"backgroundTag":"invalid-argument-value","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}