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

  1. Create a fresh registration session for the phone number being verified and use its sessionId
  2. Ensure the number passed to verify() is identical (same E.164 format) to the one that created the session
  3. Discard cached session IDs whenever the phone number changes
  4. 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

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


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)