{"record":{"id":"9d59b13afe7cb558","repo":"apache/cassandra","slug":"masking-function-s-doesn-t-belong-to-the-same-key","errorCode":null,"errorMessage":"Masking function %s doesn't belong to the same keyspace as the table %s.%s","messagePattern":"Masking function (.+?) doesn't belong to the same keyspace as the table (.+?)\\.(.+?)","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/functions/masking/ColumnMask.java","lineNumber":256,"sourceCode":"            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;\n        }\n\n        private ByteBuffer[] preparePartialArguments(String keyspace, ScalarFunction function)\n        {\n            // Note that there could be null arguments\n            ByteBuffer[] arguments = new ByteBuffer[rawPartialArguments.size()];","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/functions/masking/ColumnMask.java#L238-L274","documentation":"ColumnMask.findMaskingFunction requires that a non-native (user-defined) masking function lives in the same keyspace as the masked table. When the resolved UDF's keyspace differs from the table's keyspace, the DDL is rejected to keep masking policies keyspace-local.","triggerScenarios":"ALTER TABLE ks1.t ALTER col MASKED WITH ks2.my_mask(...) where ks2 != ks1 and my_mask is a UDF.","commonSituations":"Teams share masking UDFs from a central keyspace and reference them fully-qualified from tables in other keyspaces; native masking functions are exempt but UDFs are not.","solutions":["Create the masking UDF in the same keyspace as the table (CREATE FUNCTION ks1.my_mask ...).","Or use the built-in native masking functions, which have no keyspace restriction.","Or create the table in the keyspace that already holds the masking function."],"exampleFix":"// before\nCREATE FUNCTION shared_ks.mask_email(text) ...;\nALTER TABLE app_ks.users ALTER email MASKED WITH shared_ks.mask_email(text);\n// after\nCREATE FUNCTION app_ks.mask_email(text) ...;\nALTER TABLE app_ks.users ALTER email MASKED WITH app_ks.mask_email(text);","handlingStrategy":"validation","validationCode":"if (fnKeyspace != null && !fnKeyspace.equals(tableKeyspace) && !isNative(fnName))\n  throw new IllegalArgumentException(\"masking UDF must live in keyspace \" + tableKeyspace);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Create masking UDFs inside each table's keyspace, or use a deployment script that duplicates them per keyspace.","Use unqualified built-in masking functions to avoid keyspace coupling."],"tags":["cql","masking","keyspace","udf"],"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"}