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
- Check server logs for the 'Registration service failure' stack trace to find the root cause
- Verify the registration service is reachable (network policy, DNS, port)
- Confirm registration service config values (host, port, TLS) in the server YAML
- 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
- Monitor registration service health and alert on 5xx spikes
- Configure gRPC retries for UNAVAILABLE statuses
- Keep registration service endpoint config validated at server startup
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.
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
Related errors
- Registration service failure
- interrupted during delivery
- delivery cancelled
- failure during delivery
- could not parse already validated number
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)