apache/iceberg · warning · ServiceUnavailableException
Service unavailable
Error message
Service unavailable: %s
What it means
RESTClient's default error handler maps HTTP 503 responses to ServiceUnavailableException. The catalog service is temporarily unavailable — typically overload, maintenance, or a dependency being down. This is usually transient.
Solutions
- Retry the request with exponential backoff and jitter — 503 is expected to be transient
- Check the catalog service status/maintenance announcements
- Reduce request concurrency in your job (e.g. fewer parallel scan planning calls)
- If persistent, contact the catalog operator to check capacity and health
Example fix
// before Table table = catalog.loadTable(id); // fails on 503 during maintenance // after // configure client retry: 'rest.client.retry.max-attempts' etc., or wrap: Tasks.foreach(() -> catalog.loadTable(id)).retry(5).exponentialBackoff(100, 60000);
Defensive patterns
Strategy: retry
Try / catch
Tasks.foreach(() -> catalog.loadTable(identifier))
.retry(5)
.exponentialBackoff(200, 120000)
.throwFailureWhenFinished(); Prevention
- Always configure retry with backoff and jitter for REST catalog access
- Throttle scan planning concurrency against shared catalogs
- Track maintenance windows for your catalog service
When it happens
Trigger: HTTP 503 returned by the REST server or an intermediate gateway during any catalog request, e.g. during server restart, rolling deployment, or capacity limits.
Common situations: Query engines hammering the catalog during heavy jobs, server maintenance windows, autoscaling cold starts, or upstream dependency (metadata DB) saturation.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
Related errors
- Cannot call commit on temporary table operations
- Cannot call refresh on temporary table operations
- Failed to close HTTP client
- Failed to convert HTTP response body to string
- Failed to get status for file
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/43d4708479053f76.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java:354
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) {
LOG.warn("Unable to parse error response", x);
}
return ErrorResponse.builder().responseCode(code).withMessage(json).build();
}View on GitHub (pinned to 86d9c8fc54)