phacility/phabricator · error · PhabricatorWorkerPermanentFailureException

Worker has invalid job ID ("%s").

Error message

Worker has invalid job ID ("%s").

What it means

loadJob() takes the jobID from task data and loads the PhabricatorWorkerBulkJob via an omnipotent-user query; if no row matches, the task permanently fails with this message. Because the query runs as the omnipotent user, policies can never cause this — only the row's absence (deleted job or wrong ID) can.

Source

Thrown at src/infrastructure/daemon/workers/bulk/PhabricatorWorkerBulkJobWorker.php:43

  final protected function getTaskID() {
    $data = $this->getTaskData();
    $id = idx($data, 'taskID');
    if (!$id) {
      throw new PhabricatorWorkerPermanentFailureException(
        pht('Worker has no task ID.'));
    }
    return $id;
  }

  final protected function loadJob() {
    $id = $this->getJobID();
    $job = id(new PhabricatorWorkerBulkJobQuery())
      ->setViewer(PhabricatorUser::getOmnipotentUser())
      ->withIDs(array($id))
      ->executeOne();
    if (!$job) {
      throw new PhabricatorWorkerPermanentFailureException(
        pht('Worker has invalid job ID ("%s").', $id));
    }
    return $job;
  }

  final protected function loadTask() {
    $id = $this->getTaskID();
    $task = id(new PhabricatorWorkerBulkTask())->load($id);
    if (!$task) {
      throw new PhabricatorWorkerPermanentFailureException(
        pht('Worker has invalid task ID ("%s").', $id));
    }
    return $task;
  }

  final protected function loadActor(PhabricatorWorkerBulkJob $job) {
    $actor_phid = $job->getAuthorPHID();
    $actor = id(new PhabricatorPeopleQuery())

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Check whether the bulk job still exists (bulk job UI or the phabricator_worker_bulk_job table).
  2. If it was deleted intentionally, the permanent failure is correct — archive/dispose of the failed task.
  3. If the job should exist, restore the missing row or fix the referenced ID, then re-queue the task.
  4. Keep taskmaster throughput healthy so tasks do not outlive their jobs.
Defensive patterns

Strategy: try-catch

Validate before calling

$job = id(new PhabricatorWorkerBulkJobQuery())
  ->setViewer(PhabricatorUser::getOmnipotentUser())
  ->withIDs(array($job_id))
  ->executeOne();
if (!$job) {
  // do not queue the worker task
}

Try / catch

try {
  // run the bulk worker
} catch (PhabricatorWorkerPermanentFailureException $ex) {
  // expected when the job row is gone: log and archive the task
}

Prevention

When it happens

Trigger: The bulk job was deleted before its worker task ran; jobID in the task data points at a nonexistent row after data corruption or a partial database restore that kept worker tasks but dropped job rows.

Common situations: Admins deleting queued bulk jobs; partial restores; long queue backlogs where tasks outlive the jobs they reference.

Related errors


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