signalapp/Signal-Server · warning
return Response.status(428).build();
Error message
return Response.status(428).build();
What it means
ChallengeController's captcha verification path returns HTTP 428 (Precondition Required) when the captcha token supplied by the client fails verification at the configured score threshold. It is not an exception but a deliberate guard response signaling the client must pass a valid challenge before proceeding.
Solutions
- Obtain and submit a fresh, valid captcha token for the request
- Check captcha service configuration (site key/secret) and that tokens match the site
- Lower captchaScoreThreshold in configuration if legitimate users are rejected
- Inspect captcha service logs/scores for the failing request to confirm cause
Example fix
// before
curl -X POST /v1/challenge -d '{"captcha":"stale-token"}' -> 428
// after
request a new captcha, then
curl -X POST /v1/challenge -d '{"captcha":"fresh-valid-token"}' Defensive patterns
Strategy: retry
Validate before calling
if (captchaToken == null || captchaToken.isBlank()) {
throw new IllegalArgumentException("captcha token required");
} Try / catch
if (response.getStatus() == 428) { requestNewCaptchaAndRetry(); } Prevention
- Request a fresh captcha token for every challenge
- Tune captchaScoreThreshold so legitimate users pass
- Monitor 428 rates and captcha scores for config problems
When it happens
Trigger: POSTing a challenge response with a captcha whose score (as evaluated by the captcha service against remoteAddress/userAgent and constraints.captchaScoreThreshold()) comes back success=false.
Common situations: Stale or already-used captcha tokens; bots/low-score traffic; user behind datacenter IP causing low score; captcha service outage or misconfigured site key/secret; score threshold set too aggressively high.
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
- too few parts
- invalid captcha scheme
- invalid captcha action
- invalid captcha site-key
- 429 Too Many Requests (rate limit exceeded)
AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09).
Data as JSON: /api/errors/cf0f68f135da91a4.
Report an issue: GitHub.
Appendix: source
Thrown at service/src/main/java/org/whispersystems/textsecuregcm/controllers/ChallengeController.java:124
if (!constraints.pushPermitted()) {
return Response.status(429).build();
}
rateLimitChallengeManager.answerPushChallenge(account, pushChallengeRequest.getChallenge());
} else if (answerRequest instanceof final AnswerCaptchaChallengeRequest captchaChallengeRequest) {
tags = tags.and(CHALLENGE_TYPE_TAG, "captcha");
final String remoteAddress = (String) requestContext.getProperty(
RemoteAddressFilter.REMOTE_ADDRESS_ATTRIBUTE_NAME);
final boolean success = rateLimitChallengeManager.answerCaptchaChallenge(
account,
captchaChallengeRequest.getCaptcha(),
remoteAddress,
userAgent,
constraints.captchaScoreThreshold());
if (!success) {
return Response.status(428).build();
}
} else {
tags = tags.and(CHALLENGE_TYPE_TAG, "unrecognized");
}
} catch (final InvalidCaptchaArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST.getStatusCode(), e.getMessage()).build();
} catch (final IOException e) {
logger.error("error assessing captcha during challenge response handling", e);
return Response.status(Response.Status.SERVICE_UNAVAILABLE).build();
} finally {
Metrics.counter(CHALLENGE_RESPONSE_COUNTER_NAME, tags).increment();
}
return Response.status(200).build();
}
@POSTView on GitHub (pinned to 100ab61c82)