apache/cassandra · error · InvalidRequestException

Masking function return type is . This is different to the…

Error message

Masking function %s return type is %s. This is different to the type of the masked column %s of type %s. Masking functions can only be attached to table columns if they return the same data type as the masked column.

What it means

ColumnMask.findMaskingFunction enforces that the masking function's return type exactly equals the type of the column being masked. Because the masked value replaces the column value in results, a type-changing mask would break type expectations, so mismatched signatures are rejected.

Solutions

  1. Pick/adjust the masking function so its return type matches the column type exactly (compare CQL3Type).
  2. For UDFs, add a returns-clause overload matching the column type or create a new function with the correct return type.
  3. If the column type is wrong for your purpose, ALTER the column semantics first (note Cassandra restricts type changes).

Example fix

// before
ALTER TABLE users ALTER email MASKED WITH hash(text); // hash(blob) vs text column
// after
ALTER TABLE users ALTER email MASKED WITH mask_inner(text, 1, 3, 'x');
Defensive patterns

Strategy: validation

Validate before calling

// verify return type matches column type before DDL
Row r = session.execute("SELECT argument_types, return_type FROM system_schema.functions WHERE keyspace_name=? AND function_name=?", ks, fn).one();
if (r != null && !r.getString("return_type").equals(columnType))
  throw new IllegalArgumentException("return type " + r.getString("return_type") + " != column type " + columnType);

Prevention

When it happens

Trigger: MASKED WITH a function whose return type differs from the column type, e.g. col is int but the function returns text, or a hash masking function applied to a non-blob column without matching signature.

Common situations: Applying mask_inner(text,...) to an int column, writing a UDF returning text to mask a bigint column, or after a column type migration the previously valid mask no longer matches.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/ed1f67e39ea4e065. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/cql3/functions/masking/ColumnMask.java:262

            if (function == null)
                throw invalidRequest("Unable to find masking function for %s, " +
                                     "no declared function matches the signature %s",
                                     column, this);

            if (function.isAggregate())
                throw invalidRequest("Aggregate function %s cannot be used for masking table columns", this);

            if (function.isNative() && !(function instanceof MaskingFunction))
                throw invalidRequest("Not-masking function %s cannot be used for masking table columns", this);

            if (!function.isNative() && !function.name().keyspace.equals(keyspace))
                throw invalidRequest("Masking function %s doesn't belong to the same keyspace as the table %s.%s",
                                     this, keyspace, table);

            CQL3Type returnType = function.returnType().asCQL3Type();
            CQL3Type expectedType = type.asCQL3Type();
            if (!returnType.equals(expectedType))
                throw invalidRequest("Masking function %s return type is %s. " +
                                     "This is different to the type of the masked column %s of type %s. " +
                                     "Masking functions can only be attached to table columns " +
                                     "if they return the same data type as the masked column.",
                                     this, returnType, column, expectedType);

            return (ScalarFunction) function;
        }

        private ByteBuffer[] preparePartialArguments(String keyspace, ScalarFunction function)
        {
            // Note that there could be null arguments
            ByteBuffer[] arguments = new ByteBuffer[rawPartialArguments.size()];

            for (int i = 0; i < rawPartialArguments.size(); i++)
            {
                String term = rawPartialArguments.get(i).toString();
                AbstractType<?> type = function.argTypes().get(i + 1);
                arguments[i] = Term.asBytes(keyspace, term, type);

View on GitHub (pinned to 88fd0f6a0e)