phacility/phabricator · error · Exception

Buildkite request to target "%s" had the wrong authenticatio

Error message

Buildkite request to target "%s" had the wrong authentication token. The Buildkite pipeline and Harbormaster build step must be configured with the same token.

What it means

Authentication failure for the Buildkite webhook: the X-Buildkite-Token header sent with the delivery is not identical (compared in constant time via phutil_hashes_are_identical) to the 'webhook.token' setting stored on the Buildkite build step. The hook deliberately fails closed so a third party who learns the hook URL cannot forge build.finished events.

Source

Thrown at src/applications/harbormaster/controller/HarbormasterBuildkiteHookController.php:70

          'Harbormaster build target "%s" does not exist.',
          $target_phid));
    }

    $step = $target->getBuildStep();
    $impl = $step->getStepImplementation();
    if (!($impl instanceof HarbormasterBuildkiteBuildStepImplementation)) {
      throw new Exception(
        pht(
          'Harbormaster build target "%s" is not a Buildkite build step. '.
          'Only Buildkite steps may be updated via the Buildkite hook.',
          $target_phid));
    }

    $webhook_token = $impl->getSetting('webhook.token');
    $request_token = $request->getHTTPHeader('X-Buildkite-Token');

    if (!phutil_hashes_are_identical($webhook_token, $request_token)) {
      throw new Exception(
        pht(
          'Buildkite request to target "%s" had the wrong authentication '.
          'token. The Buildkite pipeline and Harbormaster build step must '.
          'be configured with the same token.',
          $target_phid));
    }

    $state = idx($build, 'state');
    switch ($state) {
      case 'passed':
        $message_type = HarbormasterMessageType::MESSAGE_PASS;
        break;
      default:
        $message_type = HarbormasterMessageType::MESSAGE_FAIL;
        break;
    }

    $api_method = 'harbormaster.sendmessage';

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Copy the exact webhook.token from the Harbormaster Buildkite step into the Buildkite pipeline's webhook settings (X-Buildkite-Token header), with no extra whitespace.
  2. If the token may have been pasted with whitespace, set it again cleanly on both sides.
  3. After updating, re-test with a fresh Buildkite build (old deliveries keep the old header).
  4. Never leave the step's webhook.token empty; the check will always fail.
Defensive patterns

Strategy: validation

Validate before calling

// Before enabling the hook in Buildkite, verify both sides carry the same token:
$step_token = $impl->getSetting('webhook.token');
assert(strlen($step_token) > 0); // empty token can never authenticate
// then set the identical value as the X-Buildkite-Token header in Buildkite

Try / catch

try {
  handleBuildkiteDelivery($request);
} catch (Exception $e) {
  if (preg_match('/wrong authentication token/', $e->getMessage())) {
    // config issue, not transient: alert, do not retry
  }
  throw $e;
}

Prevention

When it happens

Trigger: The Buildkite pipeline's webhook notification header/token differs from (or omits) the token saved in the Harbormaster step settings; the token was regenerated in Harbormaster but not updated in Buildkite (or vice versa); trailing whitespace/newline introduced when pasting the token; no token configured on the step at all (null never equals the header).

Common situations: Initial setup where only one side got the token; rotating tokens during a security review and forgetting one side; copying hook URLs between pipelines that use different tokens.

Understand the failure class

Related errors


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