signalapp/Signal-Server · error · ClientErrorException

403 Forbidden

Error message

403 Forbidden

What it means

updateSession throws this ClientErrorException with HTTP 403 FORBIDDEN when handlePushChallenge or handleCaptcha raises ForbiddenException, meaning a required verification challenge (push challenge or captcha) was absent, invalid, or not satisfied. The response body still includes the session state so the client can see what information remains required. This is an authorization-of-flow error, not an auth-token problem.

Solutions

  1. Inspect the 403 response body (session JSON) for requestedInformation to see which challenge is still required.
  2. Complete the required challenge first (obtain captcha token via the captcha endpoint or deliver the push challenge) and resubmit.
  3. Ensure push challenge tokens are submitted before their remoteExpirationSeconds window lapses.
  4. Verify the captcha token is generated for the correct site key/session and passed in the captcha field.
Defensive patterns

Strategy: validation

Validate before calling

const s = await getSession(sessionId);
if (s.requestedInformation.length > 0) { completeRequiredChallenges(s.requestedInformation); }

Try / catch

try { await updateSession(...); } catch (e) {
  if (e.status === 403) { const session = e.body; /* inspect requestedInformation */ }
  throw e;
}

Prevention

When it happens

Trigger: Updating a verification session where the push challenge token is present but invalid/expired (handlePushChallenge), or the captcha assessment did not pass and no alternative challenge was satisfied (handleCaptcha).

Common situations: Client skipping the captcha step; expired push challenge token delivered late; captcha solved for the wrong session/region; calling updateSession before completing prerequisite steps (requestedInformation not yet submitted).

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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

Appendix: source

Thrown at service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java:315

      verificationSession = verificationCheck.updatedSession().orElse(verificationSession);

      verificationSession = handlePushToken(pushTokenAndType, verificationSession);

      verificationSession = handlePushChallenge(updateVerificationSessionRequest, registrationServiceSession,
          verificationSession);

      verificationSession = handleCaptcha(sourceHost, updateVerificationSessionRequest, registrationServiceSession,
          verificationSession, userAgent, verificationCheck.scoreThreshold());
    } catch (final RateLimitExceededException e) {

      final Response response = buildResponseForRateLimitExceeded(verificationSession, registrationServiceSession,
          e.getRetryDuration());
      throw new ClientErrorException(response);

    } catch (final ForbiddenException e) {

      throw new ClientErrorException(Response.status(Response.Status.FORBIDDEN)
          .entity(buildResponse(registrationServiceSession, verificationSession))
          .build());

    } finally {
      // Each of the handle* methods may update requestedInformation, submittedInformation, and allowedToRequestCode,
      // and we want to be sure to store a changes, even if a later method throws
      verificationSessionManager.update(verificationSession);
    }

    return buildResponse(registrationServiceSession, verificationSession);
  }

  /**
   * If {@code pushTokenAndType} values are not {@code null}, sends a push challenge. If there is no existing push
   * challenge in the session, one will be created, set on the returned session record, and
   * {@link VerificationSession#requestedInformation()} will be updated.
   */
  private VerificationSession handlePushToken(

View on GitHub (pinned to 100ab61c82)