{"record":{"id":"fe06ede04f0c17dd","repo":"prestodb/presto","slug":"cannot-revoke-roles-s-from-s","errorCode":null,"errorMessage":"Cannot revoke roles %s from %s ","messagePattern":"Cannot revoke roles (.+?) from (.+?) ","errorType":"exception","errorClass":"AccessDeniedException","httpStatus":null,"severity":"error","filePath":"presto-spi/src/main/java/com/facebook/presto/spi/security/AccessDeniedException.java","lineNumber":412,"sourceCode":"\n    public static void denyCreateRole(String roleName)\n    {\n        throw new AccessDeniedException(format(\"Cannot create role %s\", roleName));\n    }\n\n    public static void denyDropRole(String roleName)\n    {\n        throw new AccessDeniedException(format(\"Cannot drop role %s\", roleName));\n    }\n\n    public static void denyGrantRoles(Set<String> roles, Set<PrestoPrincipal> grantees)\n    {\n        throw new AccessDeniedException(format(\"Cannot grant roles %s to %s \", roles, grantees));\n    }\n\n    public static void denyRevokeRoles(Set<String> roles, Set<PrestoPrincipal> grantees)\n    {\n        throw new AccessDeniedException(format(\"Cannot revoke roles %s from %s \", roles, grantees));\n    }\n\n    public static void denySetRole(String role)\n    {\n        throw new AccessDeniedException(format(\"Cannot set role %s\", role));\n    }\n\n    public static void denyCreateBranch(String tableName)\n    {\n        denyCreateBranch(tableName, null);\n    }\n\n    public static void denyCreateBranch(String tableName, String extraInfo)\n    {\n        throw new AccessDeniedException(format(\"Cannot create branch on table %s%s\", tableName, formatExtraInfo(extraInfo)));\n    }\n\n    public static void denyCreateTag(String tableName)","sourceCodeStart":394,"sourceCodeEnd":430,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-spi/src/main/java/com/facebook/presto/spi/security/AccessDeniedException.java#L394-L430","documentation":"Thrown by denyRevokeRoles when REVOKE ROLE ... FROM <principals> is rejected by checkCanRevokeRoles. Revoking requires the admin option on the roles involved, and the check validates each grantee. Message ends with a trailing space per its format string.","triggerScenarios":"Executing 'REVOKE <role> FROM USER/ROLE <grantee>' when the current user lacks the admin option for those roles or cannot revoke from those grantees.","commonSituations":"Offboarding scripts removing access run by non-admin service accounts; revoking a role in a catalog where you hold admin in a different catalog only; revoking grants made by another admin without admin option.","solutions":["Have a security admin with the role's ADMIN OPTION execute the REVOKE.","Obtain the admin option on the role if your governance process allows.","Verify the grantee names (USER vs ROLE) — wrong principal kind can hit denial paths.","Confirm the role actually has grants in the targeted catalog before revoking."],"exampleFix":"// before (denied)\nREVOKE finance_readers FROM USER bob; -- run by non-admin\n// after: executed by an admin holding finance_readers WITH ADMIN OPTION","handlingStrategy":"validation","validationCode":"for (String role : roles) {\n    if (!holdsRoleWithAdminOption(currentUser, role)) {\n        throw new IllegalStateException(\"REVOKE requires ADMIN OPTION on role \" + role);\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    stmt.execute(revokeSql);\n} catch (AccessDeniedException e) {\n    log.warn(\"REVOKE {} denied: {} — escalate to security admin\", roles, e.getMessage());\n}","preventionTips":["Run offboarding revokes with admin credentials.","Distinguish USER and ROLE grantees explicitly in scripts.","Reconcile existing grants before attempting revocation."],"tags":["security","roles","access-control","authorization"],"backgroundTag":"access-denied-authorization","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"}