{"record":{"id":"ff409e2631b53044","repo":"apache/iceberg","slug":"not-authorized-s","errorCode":null,"errorMessage":"Not authorized: %s","messagePattern":"Not authorized: (.+?)","errorType":"exception","errorClass":"NotAuthorizedException","httpStatus":401,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java","lineNumber":343,"sourceCode":"    public ErrorResponse parseResponse(int code, String json) {\n      try {\n        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 {","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java#L325-L361","documentation":"RESTClient's default error handler maps HTTP 401 responses to NotAuthorizedException. The server rejected the request because authentication failed or credentials were missing/expired. This is the client-side surface of the server's auth check in the Iceberg REST protocol.","triggerScenarios":"HTTP 401 returned by the REST server during any catalog request; e.g. missing Authorization header, expired OAuth2 token, or invalid credentials supplied via AuthConfig/catalog properties.","commonSituations":"Expired bearer token in a long-running Spark job, missing 'credential' or 'token' catalog property, misconfigured OAuth2 server URL, clock skew invalidating tokens, or rotating secrets without restarting the client.","solutions":["Check the catalog properties for authentication (token, credential, oauth2-server-uri) and supply a valid token","Refresh or re-obtain the OAuth2 bearer token; ensure token refresh is enabled","Verify the credential (client-id/secret) is still valid with your auth provider","Inspect the server message for details (e.g. 'invalid token', 'expired credentials')"],"exampleFix":"// before\nMap<String, String> props = Map.of(\"uri\", \"https://catalog.example.com\");\n// after\nMap<String, String> props = Map.of(\n    \"uri\", \"https://catalog.example.com\",\n    \"token\", validBearerToken);","handlingStrategy":"try-catch","validationCode":"if (props.get(\"token\") == null && props.get(\"credential\") == null) {\n  throw new IllegalArgumentException(\"Provide 'token' or 'credential' catalog property for auth\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  catalog.loadTable(identifier);\n} catch (NotAuthorizedException e) {\n  refreshTokenAndRebuildCatalog();\n}","preventionTips":["Enable automatic token refresh instead of static long-lived tokens","Rotate credentials before expiry in long-running jobs","Verify auth properties at catalog construction time"],"tags":["rest","http-401","authentication","oauth2"],"backgroundTag":"authentication-required","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"}