phacility/phabricator · error · PhabricatorWorkerPermanentFailureException

Unable to load webhook request ("%s"). It may have been garb

Error message

Unable to load webhook request ("%s"). It may have been garbage collected.

What it means

The webhook worker task carries a 'webhookRequestPHID' in its task data; when HeraldWebhookQuery cannot load that row, the task permanently fails with PhabricatorWorkerPermanentFailureException. The most common cause is the request row being garbage collected while the task was still queued, so the work it describes no longer exists.

Source

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

<?php

final class HeraldWebhookWorker
  extends PhabricatorWorker {

  protected function doWork() {
    $viewer = PhabricatorUser::getOmnipotentUser();

    $data = $this->getTaskData();
    $request_phid = idx($data, 'webhookRequestPHID');

    $request = id(new HeraldWebhookRequestQuery())
      ->setViewer($viewer)
      ->withPHIDs(array($request_phid))
      ->executeOne();
    if (!$request) {
      throw new PhabricatorWorkerPermanentFailureException(
        pht(
          'Unable to load webhook request ("%s"). It may have been '.
          'garbage collected.',
          $request_phid));
    }

    $status = $request->getStatus();
    if ($status !== HeraldWebhookRequest::STATUS_QUEUED) {
      throw new PhabricatorWorkerPermanentFailureException(
        pht(
          'Webhook request ("%s") is not in "%s" status (actual '.
          'status is "%s"). Declining call to hook.',
          $request_phid,
          HeraldWebhookRequest::STATUS_QUEUED,
          $status));
    }

    // If we're in silent mode, permanently fail the webhook request and then

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Treat the permanent failure as expected cleanup: the request is gone, so no delivery is possible; acknowledge/alert rather than retry.
  2. If delivery still matters, re-fire a fresh request with './bin/herald call-webhook --id <hook> --object <object>'.
  3. If this recurs, review webhook-request garbage collection retention so live queued requests are not collected.
  4. Prune stale queued tasks after DB restores so they do not execute against missing rows.
Defensive patterns

Strategy: try-catch

Validate before calling

// When queueing webhook work yourself, only queue for requests that exist:
$request = id(new HeraldWebhookRequestQuery())
  ->setViewer($viewer)
  ->withPHIDs(array($request_phid))
  ->executeOne();
if ($request) {
  PhabricatorWorker::scheduleTask(
    'HeraldWebhookWorker',
    array('webhookRequestPHID' => $request->getPHID()));
}

Try / catch

// In daemon/tooling that processes worker results, treat permanent failures as terminal:
try {
  $task->executeTask();
} catch (PhabricatorWorkerPermanentFailureException $ex) {
  // Request row is gone (e.g. garbage collected); log and move on. Never retry.
  phlog($ex->getMessage());
}

Prevention

When it happens

Trigger: A queued webhook call task executes after its herald_webhook_request row was garbage collected; worker queue data surviving a database restore that dropped request rows; the request row was deleted manually.

Common situations: Daemon downtime plus GC catching up; restoring a DB backup while the worker queue retains old tasks; aggressive webhook-request retention on a slow queue.

Related errors


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