signalapp/Signal-Server · error · ServerErrorException
503 Service Unavailable
Error message
503 Service Unavailable
What it means
handleCaptcha wraps an IOException from the captcha assessment call (to the captcha provider, e.g. reCAPTCHA Enterprise) in a ServerErrorException with HTTP 503 SERVICE UNAVAILABLE. This means the server could not reach or get an answer from the captcha service, so verification cannot proceed at this moment. It is a transient upstream availability problem, not a client mistake.
Solutions
- Retry the updateSession request after a short backoff — 503 here is typically transient.
- Check service logs for 'error assessing captcha during registration verification' to see the underlying IOException cause.
- Verify outbound network access and DNS from the service host to the captcha provider endpoint.
- Confirm captcha provider credentials/endpoint configuration (captcha configuration in dynamic config) are valid and current.
Defensive patterns
Strategy: retry
Try / catch
try { await updateSession(...); } catch (e) {
if (e.status === 503) { await sleep(backoff()); return updateSession(...); }
throw e;
} Prevention
- Retry transient 503 with backoff
- Monitor captcha provider status
- Ensure egress connectivity to the captcha API
- Keep provider credentials valid
When it happens
Trigger: updateSession with a captcha token while the call to the captcha assessment API throws IOException — network outage, DNS failure, TLS error, or the captcha provider returning an unreadable/unreachable response.
Common situations: Captcha provider outage or degraded network from the service host; egress firewall blocking the captcha API; expired/misconfigured service credentials causing connection resets; transient DNS failures in containerized environments.
Understand the failure class
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
Related errors
- Response body was below minimum
- Registration service failure
- registration service unavailable
- too few parts
- invalid captcha scheme
AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09).
Data as JSON: /api/errors/f2297a2cbe9bf19d.
Report an issue: GitHub.
Appendix: source
Thrown at service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java:482
Optional.of(updateVerificationSessionRequest.captcha()), sourceHost, userAgent)
.orElseThrow(() -> new ServerErrorException(Response.Status.INTERNAL_SERVER_ERROR));
Metrics.counter(CAPTCHA_ATTEMPT_COUNTER_NAME, Tags.of(
Tag.of(SUCCESS_TAG_NAME, String.valueOf(assessmentResult.isValid(captchaScoreThreshold))),
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(SCORE_TAG_NAME, assessmentResult.getScoreString())))
.increment();
CaptchaMetrics.measureCaptchaOutcome(assessmentResult.getNormalizedIntScore(),
assessmentResult.isValid(captchaScoreThreshold),
Util.getRegion(registrationServiceSession.number()),
"verification");
} catch (final IOException e) {
logger.error("error assessing captcha during registration verification", e);
throw new ServerErrorException(Response.Status.SERVICE_UNAVAILABLE, e);
} catch (InvalidCaptchaArgumentException e) {
throw new BadRequestException(e);
}
if (assessmentResult.isValid(captchaScoreThreshold)) {
final List<VerificationSession.Information> submittedInformation = new ArrayList<>(
verificationSession.submittedInformation());
submittedInformation.add(VerificationSession.Information.CAPTCHA);
final List<VerificationSession.Information> requestedInformation = new ArrayList<>(
verificationSession.requestedInformation());
// a captcha satisfies a push challenge, in case of push deliverability issues
requestedInformation.remove(VerificationSession.Information.PUSH_CHALLENGE);
final boolean allowedToRequestCode = (verificationSession.allowedToRequestCode()
|| requestedInformation.remove(VerificationSession.Information.CAPTCHA))
&& requestedInformation.isEmpty();
verificationSession = new VerificationSession(verificationSession.sessionId(),View on GitHub (pinned to 100ab61c82)