signalapp/Signal-Server · error · InvalidRegistrationSessionException
number does not match session
Error message
number does not match session
What it means
PhoneVerificationTokenManager.verifyBySessionId fetches the RegistrationServiceSession by ID and throws InvalidRegistrationSessionException("number does not match session") when the phone number of the current request does not match the number stored in the registration session (constant-time compare). This prevents using a verification session created for a different phone number.
Solutions
- Create a fresh registration session for the phone number being verified and use its sessionId
- Ensure the number passed to verify() is identical (same E.164 format) to the one that created the session
- Discard cached session IDs whenever the phone number changes
- Handle InvalidRegistrationSessionException by restarting the registration flow
Example fix
// before String sessionId = cachedSessionId; // from another number's flow registrationManager.verify(number, sessionId, token); // after RegistrationServiceSession session = registrationServiceClient.createSession(number, ...); registrationManager.verify(number, session.getSessionId(), token);
Defensive patterns
Strategy: try-catch
Validate before calling
// client-side: confirm the session belongs to this number before verifying
if (!sessionIdBelongsToNumber(sessionId, number)) { restartRegistration(number); } Try / catch
try {
registrationManager.verify(number, sessionId, token);
} catch (InvalidRegistrationSessionException e) {
// restart registration to create a session for this number
} catch (UnverifiedRegistrationSessionException e) {
// session not yet verified; prompt for code
} Prevention
- Always create a new session when the phone number changes
- Never reuse session IDs across registration attempts
- Normalize the phone number to E.164 before creating the session
When it happens
Trigger: Calling verify() with a sessionId belonging to a session created for a different phone number; client mixing up session IDs across concurrent registrations; reusing an old session ID after changing the number in the request.
Common situations: Client retries verification with a stale sessionId after restarting a registration flow for a different number; race between two devices registering different numbers; cached session ID not invalidated when the user edits the phone number.
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
- registration session is unverified
- Missing required device capability
- registration session is unverified
- recovery password could not be verified
- login purchases are not enabled
AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09).
Data as JSON: /api/errors/1a6b108cd8566f7f.
Report an issue: GitHub.
Appendix: source
Thrown at service/src/main/java/org/whispersystems/textsecuregcm/auth/PhoneVerificationTokenManager.java:99
: PhoneVerificationRequest.VerificationType.RECOVERY_PASSWORD;
switch (verificationType) {
case SESSION -> verifyBySessionId(number, sessionId);
case RECOVERY_PASSWORD -> verifyByRecoveryPassword(number, userAgent, acceptLanguage, mostRecentProxy, recoveryPassword);
}
return verificationType;
}
private void verifyBySessionId(final String number, final byte[] sessionId)
throws UnverifiedRegistrationSessionException, InvalidRegistrationSessionException, IOException {
try {
final RegistrationServiceSession session = registrationServiceClient
.getSession(sessionId, REGISTRATION_RPC_TIMEOUT)
.orElseThrow(UnverifiedRegistrationSessionException::new);
if (!MessageDigest.isEqual(number.getBytes(), session.number().getBytes())) {
throw new InvalidRegistrationSessionException("number does not match session");
}
if (!session.verified()) {
throw new UnverifiedRegistrationSessionException();
}
} catch (final StatusRuntimeException e) {
if (e.getStatus().getCode() == Status.Code.INVALID_ARGUMENT) {
throw new InvalidRegistrationSessionException(e.getMessage());
}
logger.error("Registration service failure", e);
throw new IOException("Registration service failure", e);
}
}
private void verifyByRecoveryPassword(
final String number,
@Nullable final String userAgent,
@Nullable final String acceptLanguage,View on GitHub (pinned to 100ab61c82)