You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@polaris.apache.org by "travis-bowen (via GitHub)" <gi...@apache.org> on 2026/05/11 17:59:19 UTC

[PR] fix(rate-limiter): return Iceberg ErrorResponse JSON body on HTTP 429 [polaris]

travis-bowen opened a new pull request, #4404:
URL: https://github.com/apache/polaris/pull/4404

   ## Motivation
   
   `RateLimiterFilter` previously returned HTTP 429 with an empty body and no `Content-Type` header:
   
   ```java
   ctx.abortWith(Response.status(Response.Status.TOO_MANY_REQUESTS).build());
   ```
   
   Iceberg REST clients (pyiceberg, Spark, and any third-party caller) cannot meaningfully parse this. The Iceberg Java client's `DefaultErrorHandler.parseResponse()` attempts `ErrorResponseParser.fromJson("")` on the empty body, catches the JSON parse failure, and falls back to building an `ErrorResponse` with `message = ""`. Since there is no `case 429:` in any `ErrorHandlers` switch, the result is always:
   
   ```
   RESTException("Unable to process: ")
   ```
   
   — an opaque error with no actionable context for the caller.
   
   ## Changes
   
   Add an Iceberg-compatible `ErrorResponse` JSON body to the 429 response, following the exact pattern already used by `IcebergExceptionMapper` for all other error codes:
   
   ```java
   ctx.abortWith(
       Response.status(Response.Status.TOO_MANY_REQUESTS)
           .type(MediaType.APPLICATION_JSON_TYPE)
           .entity(
               ErrorResponse.builder()
                   .responseCode(Response.Status.TOO_MANY_REQUESTS.getStatusCode())
                   .withType("RateLimitExceededException")
                   .withMessage("Rate exceeded")
                   .build())
           .build());
   ```
   
   ## Ecosystem Context
   
   The Iceberg REST OpenAPI spec does not define a 429 response schema — implementations are on their own. Surveying the major catalog implementations:
   
   | Implementation | Body on 429? | Follows Iceberg `ErrorModel`? |
   |---|---|---|
   | AWS Glue | Yes | No (AWS `{"__type": "ThrottlingException", ...}` format) |
   | Apache Nessie | Yes | No (Nessie `{"status": 429, "errorCode": "TOO_MANY_REQUESTS", ...}` format) |
   | Databricks Unity Catalog | Yes | No (Databricks `{"error_code": "RESOURCE_EXHAUSTED", ...}` format) |
   | Apache Polaris (this fix) | Yes | **Yes** |
   
   All other catalogs return *some* JSON, but none follow the Iceberg `ErrorModel` schema. With this fix, Polaris becomes the most spec-consistent implementation — clients using `ErrorResponseParser` will be able to round-trip the 429 body correctly.
   
   ## Compatibility
   
   - Existing clients that ignore the 429 body are unaffected.
   - Clients that previously hit the empty-body parse failure will now receive a well-formed `ErrorResponse`.
   - No change to status code or any other behavior.
   
   ## Tests
   
   Added `testRateLimitedResponseHasIcebergErrorBody` — a full integration test that performs a live HTTP request against a rate-limited endpoint, reads the response body, and round-trips it through `ErrorResponseParser.fromJson(...)` to prove Iceberg client compatibility. Asserts `Content-Type: application/json`, `code == 429`, `type == "RateLimitExceededException"`, and `message` contains `"Rate exceeded"`.
   
   Fixes #4402
   
   ---
   
   > This fix was developed with AI assistance (Claude). The author has reviewed the change end-to-end and takes full responsibility for correctness and ASF licensing compliance.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] fix(rate-limiter): return Iceberg ErrorResponse JSON body on HTTP 429 [polaris]

Posted by "snazy (via GitHub)" <gi...@apache.org>.
snazy commented on PR #4404:
URL: https://github.com/apache/polaris/pull/4404#issuecomment-4452453620

   Slight correction: It's `Project Nessie` not `Apache Nessie` (PR description)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] fix(rate-limiter): return Iceberg ErrorResponse JSON body on HTTP 429 [polaris]

Posted by "dimas-b (via GitHub)" <gi...@apache.org>.
dimas-b commented on code in PR #4404:
URL: https://github.com/apache/polaris/pull/4404#discussion_r3237666807


##########
runtime/service/src/main/java/org/apache/polaris/service/ratelimiter/RateLimiterFilter.java:
##########
@@ -74,7 +76,16 @@ public void filter(ContainerRequestContext ctx) throws IOException {
                         EventAttributes.REQUEST_URI,
                         ctx.getUriInfo().getAbsolutePath().toString())));
       }
-      ctx.abortWith(Response.status(Response.Status.TOO_MANY_REQUESTS).build());
+      ctx.abortWith(
+          Response.status(Response.Status.TOO_MANY_REQUESTS)
+              .type(MediaType.APPLICATION_JSON_TYPE)
+              .entity(
+                  ErrorResponse.builder()

Review Comment:
   So, that does look like an IRC error response payload... while I do not agree with this approach for Polaris' own API, this is the state of the project ATM.
   
   Therefore, it makes sense to use the same payload structure for 429 responses too.
   
   Thanks for the pointers, @travis-bowen :+1: 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] fix(rate-limiter): return Iceberg ErrorResponse JSON body on HTTP 429 [polaris]

Posted by "dimas-b (via GitHub)" <gi...@apache.org>.
dimas-b commented on code in PR #4404:
URL: https://github.com/apache/polaris/pull/4404#discussion_r3237606849


##########
runtime/service/src/main/java/org/apache/polaris/service/ratelimiter/RateLimiterFilter.java:
##########
@@ -74,7 +76,16 @@ public void filter(ContainerRequestContext ctx) throws IOException {
                         EventAttributes.REQUEST_URI,
                         ctx.getUriInfo().getAbsolutePath().toString())));
       }
-      ctx.abortWith(Response.status(Response.Status.TOO_MANY_REQUESTS).build());
+      ctx.abortWith(
+          Response.status(Response.Status.TOO_MANY_REQUESTS)
+              .type(MediaType.APPLICATION_JSON_TYPE)
+              .entity(
+                  ErrorResponse.builder()

Review Comment:
   :facepalm: TBH I'm not sure that applying `PolarisExceptionMapper` to non-IRC APIs was intentional :sweat_smile: 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] fix(rate-limiter): return Iceberg ErrorResponse JSON body on HTTP 429 [polaris]

Posted by "dimas-b (via GitHub)" <gi...@apache.org>.
dimas-b commented on code in PR #4404:
URL: https://github.com/apache/polaris/pull/4404#discussion_r3227017322


##########
runtime/service/src/main/java/org/apache/polaris/service/ratelimiter/RateLimiterFilter.java:
##########
@@ -74,7 +76,16 @@ public void filter(ContainerRequestContext ctx) throws IOException {
                         EventAttributes.REQUEST_URI,
                         ctx.getUriInfo().getAbsolutePath().toString())));
       }
-      ctx.abortWith(Response.status(Response.Status.TOO_MANY_REQUESTS).build());
+      ctx.abortWith(
+          Response.status(Response.Status.TOO_MANY_REQUESTS)
+              .type(MediaType.APPLICATION_JSON_TYPE)
+              .entity(
+                  ErrorResponse.builder()

Review Comment:
   This response form is specific to the Iceberg REST API, but the filter covers all Polaris REST API, IIRC... I'm not sure using this response form is relevant to all of them.
   
   ... especially considering that changing the response payload after we release with _some_ payload is going to be very difficult.
   
   Is there a way to restrict this to IRC endpoints?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] fix(rate-limiter): return Iceberg ErrorResponse JSON body on HTTP 429 [polaris]

Posted by "travis-bowen (via GitHub)" <gi...@apache.org>.
travis-bowen commented on code in PR #4404:
URL: https://github.com/apache/polaris/pull/4404#discussion_r3236191476


##########
runtime/service/src/main/java/org/apache/polaris/service/ratelimiter/RateLimiterFilter.java:
##########
@@ -74,7 +76,16 @@ public void filter(ContainerRequestContext ctx) throws IOException {
                         EventAttributes.REQUEST_URI,
                         ctx.getUriInfo().getAbsolutePath().toString())));
       }
-      ctx.abortWith(Response.status(Response.Status.TOO_MANY_REQUESTS).build());
+      ctx.abortWith(
+          Response.status(Response.Status.TOO_MANY_REQUESTS)
+              .type(MediaType.APPLICATION_JSON_TYPE)
+              .entity(
+                  ErrorResponse.builder()
+                      .responseCode(Response.Status.TOO_MANY_REQUESTS.getStatusCode())
+                      .withType("RateLimitExceededException")

Review Comment:
   Appreciate the thorough review! 
   I pulled the error message from the exception message I had ran into based on the implementation. 
   It's not as aligned to Iceberg as would be ideal.
   
   I'll change it to `TooManyRequestsException`
   
   Iceberg itself doesn't have this exception, but pyIceberg does and so it seems fitting and roughly aligned to what Iceberg exception might call it.
   
   https://github.com/apache/iceberg/tree/apache-iceberg-1.10.1/api/src/main/java/org/apache/iceberg/exceptions
   
   https://github.com/apache/iceberg-python/blob/a9ad3a305fb91abd7edecc029ccc4178398056fe/pyiceberg/exceptions.py#L91
   
   Open to other names though
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] fix(rate-limiter): return Iceberg ErrorResponse JSON body on HTTP 429 [polaris]

Posted by "dimas-b (via GitHub)" <gi...@apache.org>.
dimas-b commented on code in PR #4404:
URL: https://github.com/apache/polaris/pull/4404#discussion_r3237657393


##########
runtime/service/src/main/java/org/apache/polaris/service/ratelimiter/RateLimiterFilter.java:
##########
@@ -74,7 +76,16 @@ public void filter(ContainerRequestContext ctx) throws IOException {
                         EventAttributes.REQUEST_URI,
                         ctx.getUriInfo().getAbsolutePath().toString())));
       }
-      ctx.abortWith(Response.status(Response.Status.TOO_MANY_REQUESTS).build());
+      ctx.abortWith(
+          Response.status(Response.Status.TOO_MANY_REQUESTS)
+              .type(MediaType.APPLICATION_JSON_TYPE)
+              .entity(
+                  ErrorResponse.builder()

Review Comment:
   I did a quick wire capture of a duplicate "create catalog" request (Management API) and got:
   ```
       {
           "error": {
               "message": "Cannot create Catalog polaris. Catalog already exists or resolution failed",
               "type": "AlreadyExistsException",
               "code": 409
           }
       }
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] fix(rate-limiter): return Iceberg ErrorResponse JSON body on HTTP 429 [polaris]

Posted by "dimas-b (via GitHub)" <gi...@apache.org>.
dimas-b commented on PR #4404:
URL: https://github.com/apache/polaris/pull/4404#issuecomment-4445495769

   I'll add this to the Community sync agenda on May 14... will merge after the call unless other reviewers raise concerns.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] fix(rate-limiter): return Iceberg ErrorResponse JSON body on HTTP 429 [polaris]

Posted by "dimas-b (via GitHub)" <gi...@apache.org>.
dimas-b merged PR #4404:
URL: https://github.com/apache/polaris/pull/4404


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] fix(rate-limiter): return Iceberg ErrorResponse JSON body on HTTP 429 [polaris]

Posted by "dimas-b (via GitHub)" <gi...@apache.org>.
dimas-b commented on code in PR #4404:
URL: https://github.com/apache/polaris/pull/4404#discussion_r3226980610


##########
runtime/service/src/main/java/org/apache/polaris/service/ratelimiter/RateLimiterFilter.java:
##########
@@ -74,7 +76,16 @@ public void filter(ContainerRequestContext ctx) throws IOException {
                         EventAttributes.REQUEST_URI,
                         ctx.getUriInfo().getAbsolutePath().toString())));
       }
-      ctx.abortWith(Response.status(Response.Status.TOO_MANY_REQUESTS).build());
+      ctx.abortWith(
+          Response.status(Response.Status.TOO_MANY_REQUESTS)
+              .type(MediaType.APPLICATION_JSON_TYPE)
+              .entity(
+                  ErrorResponse.builder()
+                      .responseCode(Response.Status.TOO_MANY_REQUESTS.getStatusCode())
+                      .withType("RateLimitExceededException")

Review Comment:
   Is the type name of `RateLimitExceededException` significant in any way? I could not find any reference to it in the Iceberg Java code 🤔 



##########
runtime/service/src/main/java/org/apache/polaris/service/ratelimiter/RateLimiterFilter.java:
##########
@@ -74,7 +76,16 @@ public void filter(ContainerRequestContext ctx) throws IOException {
                         EventAttributes.REQUEST_URI,
                         ctx.getUriInfo().getAbsolutePath().toString())));
       }
-      ctx.abortWith(Response.status(Response.Status.TOO_MANY_REQUESTS).build());
+      ctx.abortWith(
+          Response.status(Response.Status.TOO_MANY_REQUESTS)
+              .type(MediaType.APPLICATION_JSON_TYPE)
+              .entity(
+                  ErrorResponse.builder()

Review Comment:
   This response form is specific to the Iceberg REST API, but the filter covers all Polaris REST API, IIRC... I'm not sure using this response form is relevant to all of them.
   
   ... especially changing the response payload after we release with _some_ payload is going to be very difficult.
   
   Is there a way to restrict this to IRC endpoints?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] fix(rate-limiter): return Iceberg ErrorResponse JSON body on HTTP 429 [polaris]

Posted by "dimas-b (via GitHub)" <gi...@apache.org>.
dimas-b commented on code in PR #4404:
URL: https://github.com/apache/polaris/pull/4404#discussion_r3236241860


##########
runtime/service/src/main/java/org/apache/polaris/service/ratelimiter/RateLimiterFilter.java:
##########
@@ -74,7 +76,16 @@ public void filter(ContainerRequestContext ctx) throws IOException {
                         EventAttributes.REQUEST_URI,
                         ctx.getUriInfo().getAbsolutePath().toString())));
       }
-      ctx.abortWith(Response.status(Response.Status.TOO_MANY_REQUESTS).build());
+      ctx.abortWith(
+          Response.status(Response.Status.TOO_MANY_REQUESTS)
+              .type(MediaType.APPLICATION_JSON_TYPE)
+              .entity(
+                  ErrorResponse.builder()
+                      .responseCode(Response.Status.TOO_MANY_REQUESTS.getStatusCode())
+                      .withType("RateLimitExceededException")

Review Comment:
   `TooManyRequestsException` SGTM :+1: 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] fix(rate-limiter): return Iceberg ErrorResponse JSON body on HTTP 429 [polaris]

Posted by "travis-bowen (via GitHub)" <gi...@apache.org>.
travis-bowen commented on code in PR #4404:
URL: https://github.com/apache/polaris/pull/4404#discussion_r3236491526


##########
runtime/service/src/main/java/org/apache/polaris/service/ratelimiter/RateLimiterFilter.java:
##########
@@ -74,7 +76,16 @@ public void filter(ContainerRequestContext ctx) throws IOException {
                         EventAttributes.REQUEST_URI,
                         ctx.getUriInfo().getAbsolutePath().toString())));
       }
-      ctx.abortWith(Response.status(Response.Status.TOO_MANY_REQUESTS).build());
+      ctx.abortWith(
+          Response.status(Response.Status.TOO_MANY_REQUESTS)
+              .type(MediaType.APPLICATION_JSON_TYPE)
+              .entity(
+                  ErrorResponse.builder()

Review Comment:
   So, it should be possible with changes in the PreMatching filter.
   However, it seems that we already have expectations in polaris of ErrorResponse being built even for management APIs.
   
   Here's what I found.
   https://github.com/apache/polaris/blob/main/runtime/service/src/main/java/org/apache/polaris/service/exception/PolarisExceptionMapper.java#L44-L83     
   
   What do you think about leaving this to be universal? If not I can try the filtering to Iceberg?
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] fix(rate-limiter): return Iceberg ErrorResponse JSON body on HTTP 429 [polaris]

Posted by "dimas-b (via GitHub)" <gi...@apache.org>.
dimas-b commented on code in PR #4404:
URL: https://github.com/apache/polaris/pull/4404#discussion_r3237657393


##########
runtime/service/src/main/java/org/apache/polaris/service/ratelimiter/RateLimiterFilter.java:
##########
@@ -74,7 +76,16 @@ public void filter(ContainerRequestContext ctx) throws IOException {
                         EventAttributes.REQUEST_URI,
                         ctx.getUriInfo().getAbsolutePath().toString())));
       }
-      ctx.abortWith(Response.status(Response.Status.TOO_MANY_REQUESTS).build());
+      ctx.abortWith(
+          Response.status(Response.Status.TOO_MANY_REQUESTS)
+              .type(MediaType.APPLICATION_JSON_TYPE)
+              .entity(
+                  ErrorResponse.builder()

Review Comment:
   I did a quick wire capture of a duplicate "create catalog" response (Management API) and got:
   ```
       {
           "error": {
               "message": "Cannot create Catalog polaris. Catalog already exists or resolution failed",
               "type": "AlreadyExistsException",
               "code": 409
           }
       }
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@polaris.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org