{"record":{"id":"09c36686bf5a0a37","repo":"signalapp/Signal-Server","slug":"return-response-status-503-build","errorCode":null,"errorMessage":"return Response.status(503).build();","messagePattern":"return Response\\.status\\(503\\)\\.build\\(\\);","errorType":"http","errorClass":null,"httpStatus":503,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/mappers/IOExceptionMapper.java","lineNumber":38,"sourceCode":"  public Response toResponse(IOException e) {\n    if (!(e.getCause() instanceof java.util.concurrent.TimeoutException)) {\n      logger.warn(\"IOExceptionMapper\", e);\n    } else {\n      // Some TimeoutExceptions are because the connection is idle, but are only distinguishable using the exception\n      // message\n      final String message = e.getCause().getMessage();\n      final boolean idleTimeout =\n          message != null &&\n              // org.eclipse.jetty.io.IdleTimeout\n              (message.startsWith(\"Idle timeout expired\")\n                  // org.eclipse.jetty.http2.HTTP2Session\n                  || (message.startsWith(\"Idle timeout\") && message.endsWith(\"elapsed\")));\n      if (idleTimeout) {\n        return Response.status(Response.Status.REQUEST_TIMEOUT).build();\n      }\n    }\n\n    return Response.status(503).build();\n  }\n}\n","sourceCodeStart":20,"sourceCodeEnd":41,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/mappers/IOExceptionMapper.java#L20-L41","documentation":"IOExceptionMapper maps generic java.io.IOExceptions to HTTP 503 Service Unavailable as a catch-all. Before that, it inspects the exception message: client-abort / broken-pipe style messages return 400, and Jetty 'Idle timeout ... elapsed' messages return 408 Request Timeout. The final 503 means an I/O failure occurred while handling the request and no more specific mapping applied.","triggerScenarios":"Any request handler throws an IOException not matching the early-EOF/idle-timeout patterns — e.g. failures reading request bodies, storage/attachment I/O errors, or downstream stream failures.","commonSituations":"Backend storage (S3/attachment service) connectivity problems; client disconnecting mid-upload causing IO variants not matched by the mapper's patterns; transient network faults between service and dependencies.","solutions":["Check server logs for the underlying IOException root cause (storage, network, dependency)","Verify connectivity/health of external dependencies (attachment storage, database)","If client disconnects are being misclassified as 503, extend the mapper's message patterns for that IO message","Add retries with backoff on the client side; 503 here is effectively transient"],"exampleFix":"// before\ncode = response.code(); // 503 treated as permanent failure\n// after\nif (response.code() == 503) {\n  retryWithExponentialBackoff(request, maxAttempts = 3);\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if (response.code() == 503) {\n  retryWithExponentialBackoff(request, 3); // transient IO failure server-side\n} else if (response.code() == 408 || response.code() == 400) {\n  reconnectAndResend(); // client abort/idle timeout\n}","preventionTips":["Monitor server dependency health (storage, DB) to reduce spurious 503s","Keep request uploads within idle-timeout windows","Use client-side exponential backoff on 503","Check server logs for the underlying IOException pattern"],"tags":["http","io","server-error","jaxrs"],"backgroundTag":"http-error-status","analyzedSha":"100ab61c82627582c867d19e1c0561ba2781e927","analyzedAt":"2026-09-09T13:29:47.883Z","contentChangedAt":"2026-09-09T13:29:47.883Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}