{"record":{"id":"256e8b4d63d8b690","repo":"apache/iceberg","slug":"forbidden-s","errorCode":null,"errorMessage":"Forbidden: %s","messagePattern":"Forbidden: (.+?)","errorType":"exception","errorClass":"ForbiddenException","httpStatus":403,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java","lineNumber":345,"sourceCode":"        return ErrorResponseParser.fromJson(json);\n      } catch (Exception x) {\n        LOG.warn(\"Unable to parse error response\", x);\n      }\n      return ErrorResponse.builder().responseCode(code).withMessage(json).build();\n    }\n\n    @Override\n    public void accept(ErrorResponse error) {\n      switch (error.code()) {\n        case 400:\n          if (IllegalArgumentException.class.getSimpleName().equals(error.type())) {\n            throw new IllegalArgumentException(error.message());\n          }\n          throw new BadRequestException(\"Malformed request: %s\", error.message());\n        case 401:\n          throw new NotAuthorizedException(\"Not authorized: %s\", error.message());\n        case 403:\n          throw new ForbiddenException(\"Forbidden: %s\", error.message());\n        case 405:\n        case 406:\n          break;\n        case 500:\n          throw new ServiceFailureException(\"Server error: %s: %s\", error.type(), error.message());\n        case 501:\n          throw new UnsupportedOperationException(error.message());\n        case 503:\n          throw new ServiceUnavailableException(\"Service unavailable: %s\", error.message());\n      }\n\n      throw createRESTException(error);\n    }\n  }\n\n  private static class OAuthErrorHandler extends ErrorHandler {\n    private static final ErrorHandler INSTANCE = new OAuthErrorHandler();\n","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java#L327-L363","documentation":"RESTClient's default error handler maps HTTP 403 responses to ForbiddenException. Authentication succeeded but the server refused to authorize the operation for the given principal — the user lacks permission on the resource.","triggerScenarios":"HTTP 403 returned by the REST server for any catalog operation (create/drop/rename table, namespace operations) where the authenticated identity lacks the required privilege.","commonSituations":"Service account without write access attempting commits, namespace-level ACLs blocking table creation, IAM/policy changes removing grants, or attempting admin-only operations with a read-only role.","solutions":["Inspect the server message to identify the denied resource and required privilege","Grant the authenticated principal the required permission on the namespace/table via your catalog's authorization system","Verify you are connecting with the intended credentials/role, not a shared or default account","If the operation should be allowed, check for policy misconfiguration (e.g. wrong namespace ownership)"],"exampleFix":"// before\n// user has only READ on namespace 'prod'\ncatalog.loadTable(\"prod.events\").refresh(); // ok\ncatalog.dropTable(\"prod.events\"); // Forbidden: 403\n// after\n// run drop with a principal granted TABLE_DROP on prod.events, or\n// request the privilege: GRANT DROP ON TABLE prod.events TO ROLE etl;","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  catalog.dropTable(identifier);\n} catch (ForbiddenException e) {\n  log.warn(\"Permission denied for {}: {}\", principal, e.getMessage());\n}","preventionTips":["Grant the service account the exact privileges needed for each operation","Test permission setup with a dry-run in a dev namespace","Audit IAM/policy changes that could remove grants mid-job"],"tags":["rest","http-403","authorization","permissions"],"backgroundTag":"permission-denied","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}