signalapp/Signal-Server · warning · ClientErrorException
409 Conflict (session already verified)
Error message
409 Conflict (session already verified)
What it means
requestVerificationCode throws this ClientErrorException with HTTP 409 CONFLICT when registrationServiceSession.verified() is true — the phone number has already been verified and a code cannot be requested again for that session. The response body includes the session state so the client can detect the already-verified condition and finish registration.
Solutions
- Treat the 409 as success: the number is already verified — proceed to the next registration step instead of requesting a code.
- Inspect the response body session JSON (verified flag) and update your client state machine to stop code requests once verified.
- If a new verification is genuinely needed, create a new verification session for the number.
- Guard client retry logic: don't re-call requestVerificationCode after a prior flow reported verification success.
Example fix
// before
await requestVerificationCode(sessionId, transport);
// after
const resp = await requestVerificationCode(sessionId, transport);
if (resp.status === 409) { proceedAsVerified(); return; } Defensive patterns
Strategy: type-guard
Validate before calling
const s = await getSession(sessionId);
if (s.verified) { proceedAsVerified(); return; } Type guard
function isSessionVerified(s) { return s && s.verified === true; } Try / catch
try { await requestVerificationCode(...); } catch (e) {
if (e.status === 409 && e.body?.session?.verified) { proceedAsVerified(); return; }
throw e;
} Prevention
- Check session.verified before requesting a code
- Treat 409 as a success path in your state machine
- Avoid duplicate/double-submitted requests
When it happens
Trigger: POST to request a verification code for a session whose registration service session is already marked verified — e.g. calling requestVerificationCode again after a successful verification/registration check.
Common situations: Client retry logic re-sending a code request after verification already succeeded; double-submit from a UI; re-running an idempotency-unaware script; session reuse after the number was registered on another device.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- 429 Too Many Requests or 409 Conflict (not allowed to…
- registration session is unverified
- recovery password could not be verified
- 409 Conflict
- 429 Too Many Requests (rate limit exceeded)
AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09).
Data as JSON: /api/errors/0745eed0878f0910.
Report an issue: GitHub.
Appendix: source
Thrown at service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java:597
final Optional<String> acceptLanguage,
@NotNull @Valid final VerificationCodeRequest verificationCodeRequest,
@Context final ContainerRequestContext requestContext) throws Throwable {
final RegistrationServiceSession registrationServiceSession = retrieveRegistrationServiceSession(encodedSessionId);
final VerificationSession verificationSession;
{
final VerificationSession storedVerificationSession = retrieveVerificationSession(registrationServiceSession);
verificationSession =
registrationFraudChecker.checkSendVerificationCodeAttempt(requestContext, storedVerificationSession,
registrationServiceSession.number())
.updatedSession()
.orElse(storedVerificationSession);
}
if (registrationServiceSession.verified()) {
throw new ClientErrorException(
Response.status(Response.Status.CONFLICT)
.entity(buildResponse(registrationServiceSession, verificationSession))
.build());
}
if (!verificationSession.allowedToRequestCode()) {
final Response.Status status = verificationSession.requestedInformation().isEmpty()
? Response.Status.TOO_MANY_REQUESTS
: Response.Status.CONFLICT;
throw new ClientErrorException(
Response.status(status)
.entity(buildResponse(registrationServiceSession, verificationSession))
.build());
}
final MessageTransport messageTransport = verificationCodeRequest.transport().toMessageTransport();
View on GitHub (pinned to 100ab61c82)