signalapp/Signal-Server · error · NotAuthorizedException

registration session is unverified

Error message

registration session is unverified

What it means

During changeNumber, the registration session tied to the new phone number must be verified (e.g. SMS/voice challenge completed). If the RegistrationService reports UnverifiedRegistrationSessionException, the server responds 401 NotAuthorizedException('registration session is unverified'), meaning the session exists but its verification step was never completed.

Solutions

  1. Complete the registration session verification (submit the SMS/voice code) via the registration service before calling change-number.
  2. Create a fresh registration session, verify it, then retry the change-number request with that session id.
  3. Check session expiry and restart the flow if the verification window lapsed.
  4. Ensure the session id sent matches the session used to verify the new number.

Example fix

// before
changeNumber(sessionId); // session created but code never submitted
// after
registrationClient.verifySession(sessionId, smsCode);
changeNumber(sessionId);
Defensive patterns

Strategy: retry

Validate before calling

const session = await getSession(sessionId);
if (session.status !== 'verified') throw new Error('complete session verification first');

Type guard

const isVerifiedSession = (s) => s && s.verified === true;

Try / catch

try { await changeNumber(req); } catch (e) { if (e.status === 401 && e.message.includes('unverified')) { await submitVerificationCode(sessionId, code); return changeNumber(req); } throw e; }

Prevention

When it happens

Trigger: PUT /v2/accounts/phone_number/{number} where the referenced registration session (session id in request) has not passed the required verification challenge for the new number.

Common situations: Client submitted the change-number request before the user entered the SMS code, session verification expired, or the client used a session created for a different flow/number.

Related errors


AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09). Data as JSON: /api/errors/9331dc5d7161334a. Report an issue: GitHub.

Appendix: source

Thrown at service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountControllerV2.java:138

        throw new WebApplicationException(Response.status(410)
            .type(MediaType.APPLICATION_JSON)
            .entity(new StaleDevicesResponse(e.getMismatchedDevices().staleDeviceIds()))
            .build());
      } else {
        throw new WebApplicationException(Response.status(409)
            .type(MediaType.APPLICATION_JSON_TYPE)
            .entity(new MismatchedDevicesResponse(e.getMismatchedDevices().missingDeviceIds(),
                e.getMismatchedDevices().extraDeviceIds()))
            .build());
      }
    } catch (final IllegalArgumentException e) {
      throw new BadRequestException(e);
    } catch (final MessageTooLargeException e) {
      throw new WebApplicationException(Response.Status.REQUEST_ENTITY_TOO_LARGE);
    } catch (final MessageDeliveryNotAllowedException e) {
      throw new ServiceUnavailableException();
    } catch (final UnverifiedRegistrationSessionException e) {
      throw new NotAuthorizedException("registration session is unverified");
    } catch (final InvalidRegistrationSessionException e) {
      throw new BadRequestException(e.getMessage());
    } catch (final IOException e) {
      throw new ServiceUnavailableException(e.getMessage());
    } catch (final RecoveryPasswordVerificationFailedException e) {
      throw new ForbiddenException("recovery password could not be verified");
    }
  }

  @PUT
  @Path("/phone_number_discoverability")
  @Consumes(MediaType.APPLICATION_JSON)
  @Produces(MediaType.APPLICATION_JSON)
  @Operation(summary = "Sets whether the account should be discoverable by phone number in the directory.")
  @ApiResponse(responseCode = "204", description = "The setting was successfully updated.")
  public void setPhoneNumberDiscoverability(
      @Auth AuthenticatedDevice auth,
      @NotNull @Valid PhoneNumberDiscoverabilityRequest phoneNumberDiscoverability) {

View on GitHub (pinned to 100ab61c82)