signalapp/Signal-Server · critical · ServerErrorException

500 Internal Server Error (registration service failure)

Error message

500 Internal Server Error (registration service failure)

What it means

A 500 Internal Server Error thrown by the verification controller when the registration service client throws an unexpected RuntimeException during verification-code request/creation. The controller logs 'Registration service failure' and maps it to ServerErrorException(500). It signals an unexpected downstream failure, not a client mistake.

Solutions

  1. Check server logs for the 'Registration service failure' stack trace to find the root cause
  2. Verify the registration service is reachable (network policy, DNS, port)
  3. Confirm registration service config values (host, port, TLS) in the server YAML
  4. Retry the request after the registration service recovers; the client may also retry at the gRPC level
Defensive patterns

Strategy: retry

Validate before calling

// client-side health probe before calling
const healthy = await fetch(REGISTRATION_HEALTH_URL).then(r => r.ok).catch(() => false);
if (!healthy) throw new Error('registration service unavailable, retry later');

Try / catch

try {
  await requestVerificationCode(sessionId, channel);
} catch (e) {
  if (e.status === 500) scheduleRetryWithBackoff(e); // transient server-side failure
  else throw e;
}

Prevention

When it happens

Trigger: Calling POST /v1/verification/session/{id}/code (requestVerificationCode) when the call into registrationServiceClient (e.g. sendVerificationCode) fails with a RuntimeException such as a gRPC transport error wrapped outside the expected checked exceptions.

Common situations: Registration service down or network partition between Signal server and registration service; gRPC channel closed; misconfigured registration service URI in config; DNS failures in a Kubernetes cluster after service rename.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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

Appendix: source

Thrown at service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java:664

          .map(s -> buildResponse(s, verificationSession))
          .map(verificationSessionResponse -> {
            final Response response = registrationServiceException instanceof TransportNotAllowedException
                ? Response.status(418).entity(verificationSessionResponse).build()
                : Response.status(Response.Status.CONFLICT).entity(verificationSessionResponse).build();

            return new ClientErrorException(response);
          })
          .orElseGet(NotFoundException::new);
    } catch (final RegistrationFraudException e) {
      if (dynamicConfigurationManager.getConfiguration().getRegistrationConfiguration()
          .squashDeclinedAttemptErrors()) {
        return buildResponse(registrationServiceSession, verificationSession);
      } else {
        throw e.getCause();
      }
    } catch (final RuntimeException e) {
      logger.error("Registration service failure", e);
      throw new ServerErrorException(Response.Status.INTERNAL_SERVER_ERROR);
    }

    accountsManager.getByE164(registrationServiceSession.number())
        .filter(existingAccount ->
            experimentEnrollmentManager.isEnrolled(existingAccount.getAccountIdentifier(), VERIFICATION_CODE_PUSH_NOTIFICATION_EXPERIMENT_NAME))
        .ifPresent(existingAccount -> {
          try {
            pushNotificationManager.sendVerificationCodeRequestedNotifications(existingAccount, clock.instant());
          } catch (final NotPushRegisteredException _) {
          }
        });

    Metrics.counter(CODE_REQUESTED_COUNTER_NAME, Tags.of(
            UserAgentTagUtil.getPlatformTag(userAgent),
            Tag.of(COUNTRY_CODE_TAG_NAME, Util.getCountryCode(registrationServiceSession.number())),
            Tag.of(REGION_CODE_TAG_NAME, Util.getRegion(registrationServiceSession.number())),
            Tag.of(VERIFICATION_TRANSPORT_TAG_NAME, verificationCodeRequest.transport().toString())))
        .increment();

View on GitHub (pinned to 100ab61c82)