signalapp/Signal-Server · error · IOException

registration service unavailable

Error message

registration service unavailable

What it means

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.

Solutions

  1. Check registration service availability and latency; inspect the underlying cause via getCause().
  2. Increase the future timeout if the service is consistently slow but healthy.
  3. Retry the verification request with backoff; the condition is typically transient.
  4. Verify network paths and load balancing between the services.

Example fix

// before
try {
  phoneVerificationTokenManager.verify(...);
} catch (IOException e) {
  throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
// after
try {
  phoneVerificationTokenManager.verify(...);
} catch (IOException e) {
  if (isTransient(e)) {
    return retryWithBackoff(() -> phoneVerificationTokenManager.verify(...));
  }
  throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

try {
  phoneVerificationTokenManager.verify(...);
} catch (IOException e) {
  Throwable cause = e.getCause();
  if (cause instanceof TimeoutException || cause instanceof ExecutionException) {
    retryWithBackoff(() -> verify());
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: 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).

Common situations: Registration service slow or overloaded causing future timeouts; network interruption; registration service restarted mid-request; timeout configured too tightly.

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.

Related errors


AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09). Data as JSON: /api/errors/921c08a586e23164. Report an issue: GitHub.

Appendix: source

Thrown at service/src/main/java/org/whispersystems/textsecuregcm/auth/PhoneVerificationTokenManager.java:136

      @Nullable final String mostRecentProxy,
      final byte[] recoveryPassword)
      throws InterruptedException, IOException, RecoveryPasswordVerificationFailedException {

    if (!registrationRecoveryChecker.checkRegistrationRecoveryAttempt(number, userAgent, acceptLanguage, mostRecentProxy)) {
      throw new RecoveryPasswordVerificationFailedException();
    }

    try {
      final UUID phoneNumberIdentifier = phoneNumberIdentifiers.getPhoneNumberIdentifier(number)
          .get(VERIFICATION_TIMEOUT_SECONDS, TimeUnit.SECONDS);

      final boolean verified = phoneNumberRecoveryPasswordsManager.verify(phoneNumberIdentifier, recoveryPassword);

      if (!verified) {
        throw new RecoveryPasswordVerificationFailedException();
      }
    } catch (final ExecutionException | TimeoutException e) {
      throw new IOException("registration service unavailable", e);
    }
  }
}

View on GitHub (pinned to 100ab61c82)