{"record":{"id":"f3a2f947bd7385c3","repo":"prestodb/presto","slug":"invalid-column-mask","errorCode":"INVALID_COLUMN_MASK","errorMessage":"Multiple masks for the same column found","messagePattern":"Multiple masks for the same column found","errorType":"error_code","errorClass":"PrestoException","httpStatus":null,"severity":"error","filePath":"presto-main-base/src/main/java/com/facebook/presto/security/AccessControlManager.java","lineNumber":1008,"sourceCode":"        requireNonNull(columns, \"columns is null\");\n\n        ImmutableMap.Builder<ColumnMetadata, ViewExpression> columnMasksBuilder = ImmutableMap.builder();\n\n        // connector-provided masks take precedence over global masks\n        CatalogAccessControlEntry entry = getConnectorAccessControl(transactionId, tableName.getCatalogName());\n        if (entry != null) {\n            Map<ColumnMetadata, ViewExpression> connectorMasks = entry.getAccessControl().getColumnMasks(entry.getTransactionHandle(transactionId), identity.toConnectorIdentity(tableName.getCatalogName()), context, toSchemaTableName(tableName), columns);\n            columnMasksBuilder.putAll(connectorMasks);\n        }\n\n        Map<ColumnMetadata, ViewExpression> systemMasks = systemAccessControl.getColumnMasks(identity, context, toCatalogSchemaTableName(tableName), columns);\n        columnMasksBuilder.putAll(systemMasks);\n\n        try {\n            return columnMasksBuilder.buildOrThrow();\n        }\n        catch (IllegalArgumentException exception) {\n            throw new PrestoException(INVALID_COLUMN_MASK, \"Multiple masks for the same column found\", exception);\n        }\n    }\n\n    private CatalogAccessControlEntry getConnectorAccessControl(TransactionId transactionId, String catalogName)\n    {\n        return transactionManager.getOptionalCatalogMetadata(transactionId, catalogName)\n                .map(metadata -> connectorAccessControl.get(metadata.getConnectorId()))\n                .orElse(null);\n    }\n\n    @Managed\n    @Nested\n    public CounterStat getAuthenticationSuccess()\n    {\n        return authenticationSuccess;\n    }\n\n    @Managed","sourceCodeStart":990,"sourceCodeEnd":1026,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-main-base/src/main/java/com/facebook/presto/security/AccessControlManager.java#L990-L1026","documentation":"When computing column masks, AccessControlManager merges system-level and catalog-level masks into an ImmutableSetMultimap keyed by column and calls buildOrThrow(). Presto allows at most one mask per column, so duplicate keys make buildOrThrow throw IllegalArgumentException, which is rethrown as PrestoException with code INVALID_COLUMN_MASK.","triggerScenarios":"Two or more configured column masks (from system access control and/or catalog/connector access control entries) target the same (catalog, schema, table, column) — e.g. systemMasks plus a connector mask on the same column.","commonSituations":"Layering access control configs where both a system-level file-based control and a connector-level control define a mask for the same column; duplicated rules in a JSON access control config after a merge or migration; incremental policy edits that added a second mask instead of replacing the existing one.","solutions":["Audit system and catalog access control configs and remove all but one mask per column","Consolidate the masking logic into a single mask expression that covers both requirements","Assign the masks to different scopes (e.g. keep only the system mask and delete the connector mask, or vice versa)","Catch INVALID_COLUMN_MASK at deploy/validate time and log which column is double-masked"],"exampleFix":"// before: system mask AND catalog mask both on users.email\n// after: keep a single mask\n{\n  \"system_access_control\": \"file\",\n  \"catalog_access_controls\": [{ \"name\": \"file\", \"config.properties\": \"catalog.properties\" }]\n}\n// remove the duplicate email column mask from one of the two configs","handlingStrategy":"validation","validationCode":"// detect duplicate mask targets in access control JSON before deploy\n// jq -r '.columnMasks[] | .catalog+\".\"+.schema+\".\"+.table+\".\"+.column' config.json | sort | uniq -d","typeGuard":null,"tryCatchPattern":"try {\n    masks = accessControlManager.getColumnMasks(...);\n} catch (PrestoException e) {\n    if (e.getErrorCode() == INVALID_COLUMN_MASK.toErrorCode()) {\n        log.error(\"Deduplicate column masks in access control configuration\");\n    } else { throw e; }\n}","preventionTips":["Keep column masks in a single config layer only","Run config linting that fails on duplicate (table, column) mask entries","Review access control diffs for accidentally re-added mask rules"],"tags":["presto","access-control","column-masking","security","config"],"backgroundTag":"duplicate-column-mask","analyzedSha":"55bb57d202de3b926896fa966c2c4a44c779634e","analyzedAt":"2026-09-04T12:50:26.162Z","contentChangedAt":"2026-09-04T12:50:26.162Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}