phacility/phabricator · error · PhabricatorWorkerPermanentFailureException

Worker has no task ID.

Error message

Worker has no task ID.

What it means

Bulk task workers identify the row they must process via the 'taskID' key of the worker task data; getTaskID() permanently fails the task when the key is missing or falsy. As with jobID, core always populates it, so this points at custom queueing code omitting the key.

Source

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

    return PhabricatorGlobalLock::newLock('bulktask.'.$this->getTaskID())
      ->lock(15);
  }

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

  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;
  }

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Schedule task workers with array('taskID' => $task->getID(), 'jobID' => $task->getJobID()).
  2. Queue bulk tasks through the job pipeline rather than scheduleTask() directly.
  3. Mirror core's task-data keys exactly in any custom code.

Example fix

// before
PhabricatorWorker::scheduleTask(
  'PhabricatorWorkerBulkJobTaskWorker',
  array('jobID' => $job->getID()));

// after
PhabricatorWorker::scheduleTask(
  'PhabricatorWorkerBulkJobTaskWorker',
  array(
    'jobID' => $job->getID(),
    'taskID' => $bulk_task->getID(),
  ));
Defensive patterns

Strategy: validation

Validate before calling

if (!idx($task_data, 'taskID')) {
  throw new InvalidArgumentException(
    'Bulk task worker data requires a taskID.');
}
PhabricatorWorker::scheduleTask($bulk_task_worker_class, $task_data);

Type guard

function bulk_task_data_has_task_id($data) {
  return is_array($data) && !empty($data['taskID']);
}

Prevention

When it happens

Trigger: Scheduling PhabricatorWorkerBulkJobTaskWorker (or a subclass) with a data array lacking 'taskID'; the lock name itself (bulktask.<taskID>) is even derived from this value.

Common situations: Custom schedulers that copy the create-worker's data shape (jobID only) when queuing task workers; refactors that dropped the key.

Related errors


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