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
- 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.
- Fix the receiver, then re-fire manually: './bin/herald call-webhook --id <hook> --object <object>'.
- 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
- Read the stored error type/code on the failed request row before re-firing; blind retries just fail again.
- Choose retry policies deliberately: retry-forever for recoverable receivers, limited for strict contracts.
- Alert on STATUS_FAILED transitions, not just task exceptions, since the row is the source of truth.
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
- Unable to load webhook request ("%s"). It may have been garb
- Webhook request ("%s") is not in "%s" status (actual status
- Associated hook ("%s") for webhook request ("%s") is disable
- Unable to load object ("%s") for webhook request ("%s").
- Webhook request ("%s", to "%s") failed (%s / %s). The reques
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/96469d0f80e45767.
Report an issue: GitHub.