apache/cassandra · error · UnauthorizedException
User %s has no UNMASK nor SELECT_MASKED permission on table
Error message
User %s has no UNMASK nor SELECT_MASKED permission on table %s.%s, cannot query masked columns %s
What it means
SelectStatement.authorize() enforces dynamic data masking permissions: if the user has neither UNMASK nor SELECT_MASKED on a table and the query restricts (filters on) masked columns, it throws UnauthorizedException listing the offending columns. Masking is bypassed only for users holding one of those permissions or full ALTER/DROP/SELECT-unrestricted grant; this error blocks leaking masked data via WHERE clauses.
Source
Thrown at src/java/org/apache/cassandra/cql3/statements/SelectStatement.java:350
{
state.ensureTablePermission(table, Permission.SELECT);
}
for (Function function : getFunctions())
state.ensurePermission(Permission.EXECUTE, function);
if (table.hasMaskedColumns() &&
!state.hasTablePermission(table, Permission.UNMASK) &&
!state.hasTablePermission(table, Permission.SELECT_MASKED))
{
List<ColumnMetadata> queriedMaskedColumns = table.columns()
.stream()
.filter(ColumnMetadata::isMasked)
.filter(restrictions::isRestricted)
.collect(Collectors.toList());
if (!queriedMaskedColumns.isEmpty())
throw new UnauthorizedException(format("User %s has no UNMASK nor SELECT_MASKED permission on table %s.%s, " +
"cannot query masked columns %s",
state.getUser().getName(), keyspace(), table(), queriedMaskedColumns));
}
}
public void validate(ClientState state) throws InvalidRequestException
{
if (parameters.allowFiltering && !SchemaConstants.isSystemKeyspace(table.keyspace))
Guardrails.allowFilteringEnabled.ensureEnabled(state);
}
@Override
public void validatePrepare(ClientState state)
{
Guardrails.preparedStatementsRequireParameters.guard(this, restrictions, state, table.keyspace, table.getTableName());
}
@OverrideView on GitHub (pinned to 88fd0f6a0e)
Solutions
- GRANT SELECT_MASKED ON keyspace.table TO app_role if the app must filter on masked columns
- GRANT UNMASK ON keyspace.table TO the role if it should see unmasked values entirely
- Restructure the query to not restrict on the masked column (filter on an unmasked column instead)
- Ask an administrator to review the masking policy if filtering is a legitimate need
Example fix
// before SELECT * FROM ks.t WHERE ssn = '123-45-6789'; // Unauthorized // after (as admin) GRANT SELECT_MASKED ON ks.t TO reporting_user; // then run the query as reporting_user
Defensive patterns
Strategy: try-catch
Validate before calling
const perms = await listEffectivePermissions(user, 'ks', 't'); if (!perms.includes('UNMASK') && !perms.includes('SELECT_MASKED') && queryFiltersOnMaskedColumns) throw new Error('Request SELECT_MASKED grant first'); Try / catch
try { return session.execute(query); } catch (e) { if (e instanceof UnauthorizedException || /UNMASK nor SELECT_MASKED/.test(e.message)) { requestMaskedPermissionOrRewriteQuery(); } else throw e; } Prevention
- Grant SELECT_MASKED to application roles that legitimately filter on masked columns
- Track which columns carry MASKED WITH policies and avoid filtering on them from restricted roles
- Review queries after security hardening changes that introduce masking
When it happens
Trigger: Executing SELECT ... WHERE masked_col = x as a user without UNMASK or SELECT_MASKED permission on the table, when masked_col has a masking policy attached (CREATE TABLE ... col int MASKED WITH ...).
Common situations: Non-admin users querying tables where a DBA recently added column masking; applications running under restricted roles after a security hardening rollout; dashboards filtering on columns that are now masked.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Only superusers are allowed to alter access from CIDR groups
- User %s does not have sufficient privileges to perform the r
- Granting permissions on system keyspaces is strictly limited
- %s doesn't exist
- Role %s doesn't exist
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/fe018b2d90ae6835.
Report an issue: GitHub.