{"record":{"id":"37edd3a76da73465","repo":"apache/cassandra","slug":"cannot-replace-function-s-the-new-return-type","errorCode":null,"errorMessage":"Cannot replace function '%s', the new return type %s is not compatible with the return type %s of existing function","messagePattern":"Cannot replace function '(.+?)', the new return type (.+?) is not compatible with the return type (.+?) of existing function","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/schema/CreateFunctionStatement.java","lineNumber":157,"sourceCode":"            if (existingFunction.isAggregate())\n                throw ire(\"Function '%s' cannot replace an aggregate\", functionName);\n\n            if (ifNotExists)\n                return schema;\n\n            if (!orReplace)\n                throw ire(\"Function '%s' already exists\", functionName);\n\n            if (calledOnNullInput != ((UDFunction) existingFunction).isCalledOnNullInput())\n            {\n                throw ire(\"Function '%s' must have %s directive\",\n                          functionName,\n                          calledOnNullInput ? \"CALLED ON NULL INPUT\" : \"RETURNS NULL ON NULL INPUT\");\n            }\n\n            if (!returnType.isCompatibleWith(existingFunction.returnType()))\n            {\n                throw ire(\"Cannot replace function '%s', the new return type %s is not compatible with the return type %s of existing function\",\n                          functionName,\n                          returnType.asCQL3Type(),\n                          existingFunction.returnType().asCQL3Type());\n            }\n\n            // TODO: update dependent aggregates\n        }\n\n        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.userFunctions.withAddedOrUpdated(function)));\n    }\n\n    SchemaChange schemaChangeEvent(KeyspacesDiff diff)\n    {\n        assert diff.altered.size() == 1;\n        FunctionsDiff<UDFunction> udfsDiff = diff.altered.get(0).udfs;\n\n        assert udfsDiff.created.size() + udfsDiff.altered.size() == 1;\n        boolean created = !udfsDiff.created.isEmpty();","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/schema/CreateFunctionStatement.java#L139-L175","documentation":"When replacing an existing UDF with OR REPLACE, the new return type must be compatible with (usable in place of) the existing function's return type, so dependent aggregates and clients remain valid. If returnType.isCompatibleWith(existing.returnType()) is false, the statement throws this InvalidRequestException.","triggerScenarios":"CREATE OR REPLACE FUNCTION changing the RETURNS type incompatibly, e.g. from int to text, or between incompatible UDTs.","commonSituations":"Evolving a UDF's return type while aggregates or application code depend on it; tools regenerating function DDL with changed types; accidental type changes when copying definitions.","solutions":["Keep the return type identical to the existing function's type","Drop dependent aggregates, drop and recreate the function with the new type, then recreate the aggregates","Check the current type in system_schema.functions before replacing"],"exampleFix":"// before\nCREATE OR REPLACE FUNCTION ks.f(int) RETURNS NULL ON NULL INPUT RETURNS text ...  -- existing returns int\n// after\nCREATE OR REPLACE FUNCTION ks.f(int) RETURNS NULL ON NULL INPUT RETURNS int ...","handlingStrategy":"validation","validationCode":"ResultSet rs = session.execute(\"SELECT return_type FROM system_schema.functions WHERE keyspace_name = ? AND function_name = ?\", ks, fn);\nString existingReturn = rs.all().get(0).getString(0);\nif (!existingReturn.equalsIgnoreCase(newReturnType)) throw new IllegalStateException(\"Return type changed: \" + existingReturn + \" -> \" + newReturnType);","typeGuard":null,"tryCatchPattern":"try { session.execute(stmt); } catch (InvalidRequestException e) { if (e.getMessage().contains(\"not compatible with the return type\")) { /* keep original type or plan drop/recreate */ } else throw e; }","preventionTips":["Treat UDF return types as immutable API surface","Plan type changes via drop/recreate with dependent aggregate handling","Compare against system_schema.functions before OR REPLACE"],"tags":["cql","udf","type-mismatch","or-replace"],"backgroundTag":"type-mismatch","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"}