{"record":{"id":"e8de6988454e2086","repo":"apache/cassandra","slug":"aggregate-function-s-cannot-be-used-for-masking-t","errorCode":null,"errorMessage":"Aggregate function %s cannot be used for masking table columns","messagePattern":"Aggregate function (.+?) cannot be used for masking table columns","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/functions/masking/ColumnMask.java","lineNumber":250,"sourceCode":"            ByteBuffer[] partialArguments = preparePartialArguments(keyspace, function);\n            return new ColumnMask(function, partialArguments);\n        }\n\n        private ScalarFunction findMaskingFunction(String keyspace, String table, ColumnIdentifier column, AbstractType<?> type, UserFunctions functions)\n        {\n            List<AssignmentTestable> args = new ArrayList<>(rawPartialArguments.size() + 1);\n            args.add(type);\n            args.addAll(rawPartialArguments);\n\n            Function function = FunctionResolver.get(keyspace, name, args, keyspace, table, type, functions);\n\n            if (function == null)\n                throw invalidRequest(\"Unable to find masking function for %s, \" +\n                                     \"no declared function matches the signature %s\",\n                                     column, this);\n\n            if (function.isAggregate())\n                throw invalidRequest(\"Aggregate function %s cannot be used for masking table columns\", this);\n\n            if (function.isNative() && !(function instanceof MaskingFunction))\n                throw invalidRequest(\"Not-masking function %s cannot be used for masking table columns\", this);\n\n            if (!function.isNative() && !function.name().keyspace.equals(keyspace))\n                throw invalidRequest(\"Masking function %s doesn't belong to the same keyspace as the table %s.%s\",\n                                     this, keyspace, table);\n\n            CQL3Type returnType = function.returnType().asCQL3Type();\n            CQL3Type expectedType = type.asCQL3Type();\n            if (!returnType.equals(expectedType))\n                throw invalidRequest(\"Masking function %s return type is %s. \" +\n                                     \"This is different to the type of the masked column %s of type %s. \" +\n                                     \"Masking functions can only be attached to table columns \" +\n                                     \"if they return the same data type as the masked column.\",\n                                     this, returnType, column, expectedType);\n\n            return (ScalarFunction) function;","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/functions/masking/ColumnMask.java#L232-L268","documentation":"ColumnMask.findMaskingFunction rejects aggregate functions used in a MASKED WITH clause. Masking requires a ScalarFunction that transforms a single column value; aggregates consume many rows and cannot be applied per-value. The check is function.isAggregate() after resolution.","triggerScenarios":"MASKED WITH sum(int) or any other aggregate (count, avg, max, ...) attached to a column in CREATE TABLE / ALTER TABLE masked-column DDL.","commonSituations":"Developer assumes any CQL function can mask a column and picks an aggregate like sum() or count() instead of the intended masking built-ins.","solutions":["Replace the aggregate with a dedicated masking function such as mask_null, mask_default, mask_inner, mask_outer, or hash.","If custom masking is needed, write a scalar UDF in the same keyspace that returns the same type as the column."],"exampleFix":"// before\nALTER TABLE users ALTER age MASKED WITH sum(age);\n// after\nALTER TABLE users ALTER age MASKED WITH mask_default(int);","handlingStrategy":"validation","validationCode":"Set<String> aggregates = Set.of(\"count\",\"sum\",\"avg\",\"max\",\"min\");\nif (aggregates.contains(fnName.toLowerCase())) throw new IllegalArgumentException(\"aggregates cannot mask columns\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only use functions documented as masking functions in MASKED WITH clauses.","Review DDL migrations with a lint rule that whitelists masking function names."],"tags":["cql","masking","aggregate-function","unsupported-operation"],"backgroundTag":"unsupported-operation","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"}