phacility/phabricator · error · PhabricatorWorkerPermanentFailureException

Webhook request ("%s", to "%s") failed (%s / %s). The reques

Error message

Webhook request ("%s", to "%s") failed (%s / %s). The request will not be retried.

What it means

The terminal failure branch of webhook delivery: the request failed (timeout or error) and its retry mode is not RETRY_FOREVER, so the worker sets STATUS_FAILED, saves the request with the error details, and throws PhabricatorWorkerPermanentFailureException so the task is dropped after this attempt. The request row keeps the error type/code for inspection in the UI.

Source

Thrown at src/applications/herald/worker/HeraldWebhookWorker.php:232

        ->setLastRequestResult(HeraldWebhookRequest::RESULT_FAIL);

      if ($should_retry) {
        $request->save();

        throw new Exception(
          pht(
            'Webhook request ("%s", to "%s") failed (%s / %s). The request '.
            'will be retried.',
            $request->getPHID(),
            $uri,
            $error_type,
            $error_code));
      } else {
        $request
          ->setStatus(HeraldWebhookRequest::STATUS_FAILED)
          ->save();

        throw new PhabricatorWorkerPermanentFailureException(
          pht(
            'Webhook request ("%s", to "%s") failed (%s / %s). The request '.
            'will not be retried.',
            $request->getPHID(),
            $uri,
            $error_type,
            $error_code));
      }
    } else {
      $request
        ->setLastRequestResult(HeraldWebhookRequest::RESULT_OKAY)
        ->setStatus(HeraldWebhookRequest::STATUS_SENT)
        ->save();
    }
  }

  private function failRequest(
    HeraldWebhookRequest $request,

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Open the failed request in the Webhooks UI and read the recorded error type/code (the %s / %s in the message) to identify HTTP vs timeout failure.
  2. Fix the receiver, then re-fire manually: './bin/herald call-webhook --id <hook> --object <object>'.
  3. For flaky endpoints, switch the hook's retry mode to retry-forever so this becomes the retriable path instead.
Defensive patterns

Strategy: try-catch

Validate before calling

// Before firing, confirm the receiver is healthy so the final attempt is not wasted:
// (optional) probe the endpoint; then:
// ./bin/herald call-webhook --id <id> --object <object>

Try / catch

try {
  // trigger webhook delivery (worker or call-webhook)
} catch (PhabricatorWorkerPermanentFailureException $ex) {
  // delivery exhausted its retry budget: alert, surface request error in UI
  $this->logPermanentWebhookFailure($ex);
}

Prevention

When it happens

Trigger: The endpoint failed on a hook with a limited or no-retry policy (e.g. a single attempt); the last permitted retry failed; permanent endpoint errors such as 404 or TLS mismatches.

Common situations: Misconfigured receiver URL; endpoint moved or dropped the route; hooks configured for limited retries receiving persistent 5xx.

Related errors


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