{"record":{"id":"921c08a586e23164","repo":"signalapp/Signal-Server","slug":"registration-service-unavailable","errorCode":null,"errorMessage":"registration service unavailable","messagePattern":"registration service unavailable","errorType":"http","errorClass":"IOException","httpStatus":503,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/auth/PhoneVerificationTokenManager.java","lineNumber":136,"sourceCode":"      @Nullable final String mostRecentProxy,\n      final byte[] recoveryPassword)\n      throws InterruptedException, IOException, RecoveryPasswordVerificationFailedException {\n\n    if (!registrationRecoveryChecker.checkRegistrationRecoveryAttempt(number, userAgent, acceptLanguage, mostRecentProxy)) {\n      throw new RecoveryPasswordVerificationFailedException();\n    }\n\n    try {\n      final UUID phoneNumberIdentifier = phoneNumberIdentifiers.getPhoneNumberIdentifier(number)\n          .get(VERIFICATION_TIMEOUT_SECONDS, TimeUnit.SECONDS);\n\n      final boolean verified = phoneNumberRecoveryPasswordsManager.verify(phoneNumberIdentifier, recoveryPassword);\n\n      if (!verified) {\n        throw new RecoveryPasswordVerificationFailedException();\n      }\n    } catch (final ExecutionException | TimeoutException e) {\n      throw new IOException(\"registration service unavailable\", e);\n    }\n  }\n}\n","sourceCodeStart":118,"sourceCodeEnd":140,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/auth/PhoneVerificationTokenManager.java#L118-L140","documentation":"verifyByRecoveryPassword verifies a phone-number recovery password via an async call to the registration service. If that future fails with ExecutionException or TimeoutException, the failure is translated into an IOException with message 'registration service unavailable', signaling a transient backend problem rather than a wrong recovery password.","triggerScenarios":"Calling verify() with a recovery password while the async registration-service lookup throws (wrapped in ExecutionException) or does not complete within the configured timeout (TimeoutException).","commonSituations":"Registration service slow or overloaded causing future timeouts; network interruption; registration service restarted mid-request; timeout configured too tightly.","solutions":["Check registration service availability and latency; inspect the underlying cause via getCause().","Increase the future timeout if the service is consistently slow but healthy.","Retry the verification request with backoff; the condition is typically transient.","Verify network paths and load balancing between the services."],"exampleFix":"// before\ntry {\n  phoneVerificationTokenManager.verify(...);\n} catch (IOException e) {\n  throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);\n}\n// after\ntry {\n  phoneVerificationTokenManager.verify(...);\n} catch (IOException e) {\n  if (isTransient(e)) {\n    return retryWithBackoff(() -> phoneVerificationTokenManager.verify(...));\n  }\n  throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);\n}","handlingStrategy":"retry","validationCode":"null","typeGuard":"null","tryCatchPattern":"try {\n  phoneVerificationTokenManager.verify(...);\n} catch (IOException e) {\n  Throwable cause = e.getCause();\n  if (cause instanceof TimeoutException || cause instanceof ExecutionException) {\n    retryWithBackoff(() -> verify());\n  } else {\n    throw e;\n  }\n}","preventionTips":["Size the async timeout above p99 registration-service latency","Distinguish wrong-password failures (RecoveryPasswordVerificationFailedException) from availability failures before retrying","Add circuit breakers around registration-service calls","Alert on rising ExecutionException/TimeoutException rates"],"tags":["network","timeout","async","backend-service"],"backgroundTag":"request-timeout","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"}