{"record":{"id":"43d4708479053f76","repo":"apache/iceberg","slug":"service-unavailable-s","errorCode":null,"errorMessage":"Service unavailable: %s","messagePattern":"Service unavailable: (.+?)","errorType":"exception","errorClass":"ServiceUnavailableException","httpStatus":503,"severity":"warning","filePath":"core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java","lineNumber":354,"sourceCode":"      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\n    @Override\n    public ErrorResponse parseResponse(int code, String json) {\n      try {\n        return OAuthErrorResponseParser.fromJson(code, 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    }","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java#L336-L372","documentation":"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.","triggerScenarios":"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.","commonSituations":"Query engines hammering the catalog during heavy jobs, server maintenance windows, autoscaling cold starts, or upstream dependency (metadata DB) saturation.","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"],"exampleFix":"// before\nTable table = catalog.loadTable(id); // fails on 503 during maintenance\n// after\n// configure client retry: 'rest.client.retry.max-attempts' etc., or wrap:\nTasks.foreach(() -> catalog.loadTable(id)).retry(5).exponentialBackoff(100, 60000);","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"Tasks.foreach(() -> catalog.loadTable(identifier))\n    .retry(5)\n    .exponentialBackoff(200, 120000)\n    .throwFailureWhenFinished();","preventionTips":["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"],"tags":["rest","http-503","availability","transient"],"backgroundTag":"request-timeout","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"}