phacility/phabricator · error · ConduitException

ERR-BAD-TOKEN

ERR-BAD-TOKEN

Error message

ERR-BAD-TOKEN

What it means

conduit.getcertificate looks up the trimmed 'token' value in phabricator_conduit_certificate_token and requires the row to be no older than 15 minutes. A missing row (unknown token) or an expired one (>900s old) both yield 'ERR-BAD-TOKEN', and the failure is logged against the requesting IP.

Source

Thrown at src/applications/conduit/method/ConduitGetCertificateConduitAPIMethod.php:59

  protected function execute(ConduitAPIRequest $request) {
    $failed_attempts = PhabricatorUserLog::loadRecentEventsFromThisIP(
      PhabricatorConduitCertificateFailureUserLogType::LOGTYPE,
      60 * 5);

    if (count($failed_attempts) > 5) {
      $this->logFailure($request);
      throw new ConduitException('ERR-RATE-LIMIT');
    }

    $token = $request->getValue('token');
    $info = id(new PhabricatorConduitCertificateToken())->loadOneWhere(
      'token = %s',
      trim($token));

    if (!$info || $info->getDateCreated() < time() - (60 * 15)) {
      $this->logFailure($request, $info);
      throw new ConduitException('ERR-BAD-TOKEN');
    } else {
      $log = PhabricatorUserLog::initializeNewLog(
          $request->getUser(),
          $info->getUserPHID(),
          PhabricatorConduitCertificateUserLogType::LOGTYPE)
        ->save();
    }

    $user = id(new PhabricatorUser())->loadOneWhere(
      'phid = %s',
      $info->getUserPHID());
    if (!$user) {
      throw new Exception(pht('Certificate token points to an invalid user!'));
    }

    return array(
      'username'    => $user->getUserName(),
      'certificate' => $user->getConduitCertificate(),

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Generate a new token and call conduit.getcertificate with it immediately, within the 15-minute validity window.
  2. Verify the token is transmitted intact (no trailing newline/whitespace; it is trimmed server-side but must otherwise match exactly).
  3. If failures repeat, stop quickly: five failures per IP per 5 minutes trigger ERR-RATE-LIMIT.
Defensive patterns

Strategy: retry

Try / catch

try {
  $result = $call->execute();
} catch (ConduitException $ex) {
  if ($ex->getMessage() === 'ERR-BAD-TOKEN') {
    // Mint a NEW token and retry immediately once; old tokens cannot be revived.
  }
}

Prevention

When it happens

Trigger: Delaying more than 15 minutes between generating the token and redeeming it; reusing a token from a previous session; a token truncated or altered by shell quoting/whitespace.

Common situations: Debugging a client step-by-step so the token expires in between; caching tokens in scripts or docs instead of minting them fresh per run.

Related errors


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