apache/iceberg · error · ServiceFailureException

Server error

Error message

Server error: %s: %s

What it means

RESTClient's default error handler maps HTTP 500 responses to ServiceFailureException. The catalog server encountered an unexpected internal error while processing the request; the message includes the server-provided error type and message to aid server-side debugging.

Solutions

  1. Read the server-provided error type and message; they usually pinpoint the server-side failure
  2. Retry the request with backoff — many 500s are transient backend issues
  3. Check server logs and health status of the catalog service
  4. If reproducible, report to the catalog operator with the request details and error type

Example fix

// before
Table table = catalog.loadTable(identifier); // throws ServiceFailureException on 500
// after
Retry.callerRetry -> use backoff:
try {
  Table table = catalog.loadTable(identifier);
} catch (ServiceFailureException e) {
  // retry with exponential backoff, or fail over to a replica catalog
}
Defensive patterns

Strategy: retry

Try / catch

Tasks.foreach(() -> catalog.loadTable(identifier))
    .retry(3)
    .exponentialBackoff(100, 60000)
    .throwFailureWhenFinished();

Prevention

When it happens

Trigger: HTTP 500 returned by the REST server during any catalog request — server bug, backend store failure (e.g. underlying metadata DB or object store error), or unhandled exception server-side.

Common situations: Catalog backend outage or misconfiguration, server version bug triggered by a specific request shape, resource exhaustion on the server, or corrupted metadata causing server-side NPEs.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/4aacfca8338ace65. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java:350

    }

    @Override
    public void accept(ErrorResponse error) {
      switch (error.code()) {
        case 400:
          if (IllegalArgumentException.class.getSimpleName().equals(error.type())) {
            throw new IllegalArgumentException(error.message());
          }
          throw new BadRequestException("Malformed request: %s", error.message());
        case 401:
          throw new NotAuthorizedException("Not authorized: %s", error.message());
        case 403:
          throw new ForbiddenException("Forbidden: %s", error.message());
        case 405:
        case 406:
          break;
        case 500:
          throw new ServiceFailureException("Server error: %s: %s", error.type(), error.message());
        case 501:
          throw new UnsupportedOperationException(error.message());
        case 503:
          throw new ServiceUnavailableException("Service unavailable: %s", error.message());
      }

      throw createRESTException(error);
    }
  }

  private static class OAuthErrorHandler extends ErrorHandler {
    private static final ErrorHandler INSTANCE = new OAuthErrorHandler();

    @Override
    public ErrorResponse parseResponse(int code, String json) {
      try {
        return OAuthErrorResponseParser.fromJson(code, json);
      } catch (Exception x) {

View on GitHub (pinned to 86d9c8fc54)