phacility/phabricator · error · Exception

Invalid response token for this challenge: token digest does

Error message

Invalid response token for this challenge: token digest does not match stored digest.

What it means

PhabricatorAuthChallenge::setResponseToken() digests the offered response token with PhabricatorHash::digestWithNamedKey(). If the challenge already carries a responseDigest (persisted on an earlier leg), the fresh digest must satisfy phutil_hashes_are_identical() against the stored one; any difference means the response token changed between requests and the exception fires.

Source

Thrown at src/applications/auth/storage/PhabricatorAuthChallenge.php:213

        pht(
          'This challenge already has a response token; you can not '.
          'set a new response token.'));
    }

    if (preg_match('/ /', $token->openEnvelope())) {
      throw new Exception(
        pht(
          'The response token for this challenge is invalid: response '.
          'tokens may not include spaces.'));
    }

    $digest = PhabricatorHash::digestWithNamedKey(
      $token->openEnvelope(),
      self::TOKEN_DIGEST_KEY);

    if ($this->responseDigest !== null) {
      if (!phutil_hashes_are_identical($digest, $this->responseDigest)) {
        throw new Exception(
          pht(
            'Invalid response token for this challenge: token digest does '.
            'not match stored digest.'));
      }
    } else {
      $this->responseDigest = $digest;
    }

    $this->responseToken = $token;

    return $this;
  }

  public function getResponseToken() {
    return $this->responseToken;
  }

  public function setResponseDigest($value) {

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Restart the authentication/MFA flow to obtain fresh challenges and matching response tokens
  2. Make the client submit each challenge response exactly once, unmodified
  3. Guard forms against double submission (disable submit on click) so a second, different token never reaches the server
Defensive patterns

Strategy: try-catch

Try / catch

try {
  $challenge->setResponseToken($token_envelope);
} catch (Exception $ex) {
  // Digest mismatch: the flow's state changed (replay/double submit).
  // Abort this attempt and restart the MFA flow - do not resubmit the
  // same challenge with another minted token.
  return $this->newDialog()
    ->setTitle(pht('MFA Session Expired'))
    ->addSubmitButton(pht('Restart')); 
}

Prevention

When it happens

Trigger: Calling setResponseToken() with a different token than the one already recorded for the challenge - replaying an old MFA form after the challenge was answered, double submission where the client minted a new token, or a client that regenerates tokens per attempt against the same stored challenge.

Common situations: Back-button resubmission of an MFA prompt; JS double-fire submitting two different token values; scripts replaying captured MFA responses; concurrent attempts on one challenge.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/9b43bd5c4560679d. Report an issue: GitHub.