{"record":{"id":"1a6b108cd8566f7f","repo":"signalapp/Signal-Server","slug":"number-does-not-match-session","errorCode":null,"errorMessage":"number does not match session","messagePattern":"number does not match session","errorType":"exception","errorClass":"InvalidRegistrationSessionException","httpStatus":null,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/auth/PhoneVerificationTokenManager.java","lineNumber":99,"sourceCode":"        : PhoneVerificationRequest.VerificationType.RECOVERY_PASSWORD;\n\n    switch (verificationType) {\n      case SESSION -> verifyBySessionId(number, sessionId);\n      case RECOVERY_PASSWORD -> verifyByRecoveryPassword(number, userAgent, acceptLanguage, mostRecentProxy, recoveryPassword);\n    }\n\n    return verificationType;\n  }\n\n  private void verifyBySessionId(final String number, final byte[] sessionId)\n      throws UnverifiedRegistrationSessionException, InvalidRegistrationSessionException, IOException {\n    try {\n      final RegistrationServiceSession session = registrationServiceClient\n          .getSession(sessionId, REGISTRATION_RPC_TIMEOUT)\n          .orElseThrow(UnverifiedRegistrationSessionException::new);\n\n      if (!MessageDigest.isEqual(number.getBytes(), session.number().getBytes())) {\n        throw new InvalidRegistrationSessionException(\"number does not match session\");\n      }\n      if (!session.verified()) {\n        throw new UnverifiedRegistrationSessionException();\n      }\n    } catch (final StatusRuntimeException e) {\n      if (e.getStatus().getCode() == Status.Code.INVALID_ARGUMENT) {\n        throw new InvalidRegistrationSessionException(e.getMessage());\n      }\n\n      logger.error(\"Registration service failure\", e);\n      throw new IOException(\"Registration service failure\", e);\n    }\n  }\n\n  private void verifyByRecoveryPassword(\n      final String number,\n      @Nullable final String userAgent,\n      @Nullable final String acceptLanguage,","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/auth/PhoneVerificationTokenManager.java#L81-L117","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nString sessionId = cachedSessionId; // from another number's flow\nregistrationManager.verify(number, sessionId, token);\n// after\nRegistrationServiceSession session = registrationServiceClient.createSession(number, ...);\nregistrationManager.verify(number, session.getSessionId(), token);","handlingStrategy":"try-catch","validationCode":"// client-side: confirm the session belongs to this number before verifying\nif (!sessionIdBelongsToNumber(sessionId, number)) { restartRegistration(number); }","typeGuard":null,"tryCatchPattern":"try {\n  registrationManager.verify(number, sessionId, token);\n} catch (InvalidRegistrationSessionException e) {\n  // restart registration to create a session for this number\n} catch (UnverifiedRegistrationSessionException e) {\n  // session not yet verified; prompt for code\n}","preventionTips":["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"],"tags":["phone-verification","session-mismatch","registration"],"backgroundTag":"invalid-state-transition","analyzedSha":"100ab61c82627582c867d19e1c0561ba2781e927","analyzedAt":"2026-09-09T13:29:47.883Z","contentChangedAt":"2026-09-09T13:29:47.883Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}