{"record":{"id":"ce8a54416a20a393","repo":"phacility/phabricator","slug":"worker-has-no-task-id","errorCode":null,"errorMessage":"Worker has no task ID.","messagePattern":"Worker has no task ID\\.","errorType":"exception","errorClass":"PhabricatorWorkerPermanentFailureException","httpStatus":null,"severity":"error","filePath":"src/infrastructure/daemon/workers/bulk/PhabricatorWorkerBulkJobWorker.php","lineNumber":30,"sourceCode":"    return PhabricatorGlobalLock::newLock('bulktask.'.$this->getTaskID())\n      ->lock(15);\n  }\n\n  final protected function getJobID() {\n    $data = $this->getTaskData();\n    $id = idx($data, 'jobID');\n    if (!$id) {\n      throw new PhabricatorWorkerPermanentFailureException(\n        pht('Worker has no job ID.'));\n    }\n    return $id;\n  }\n\n  final protected function getTaskID() {\n    $data = $this->getTaskData();\n    $id = idx($data, 'taskID');\n    if (!$id) {\n      throw new PhabricatorWorkerPermanentFailureException(\n        pht('Worker has no task ID.'));\n    }\n    return $id;\n  }\n\n  final protected function loadJob() {\n    $id = $this->getJobID();\n    $job = id(new PhabricatorWorkerBulkJobQuery())\n      ->setViewer(PhabricatorUser::getOmnipotentUser())\n      ->withIDs(array($id))\n      ->executeOne();\n    if (!$job) {\n      throw new PhabricatorWorkerPermanentFailureException(\n        pht('Worker has invalid job ID (\"%s\").', $id));\n    }\n    return $job;\n  }\n","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/infrastructure/daemon/workers/bulk/PhabricatorWorkerBulkJobWorker.php#L12-L48","documentation":"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.","triggerScenarios":"Scheduling PhabricatorWorkerBulkJobTaskWorker (or a subclass) with a data array lacking 'taskID'; the lock name itself (bulktask.<taskID>) is even derived from this value.","commonSituations":"Custom schedulers that copy the create-worker's data shape (jobID only) when queuing task workers; refactors that dropped the key.","solutions":["Schedule task workers with array('taskID' => $task->getID(), 'jobID' => $task->getJobID()).","Queue bulk tasks through the job pipeline rather than scheduleTask() directly.","Mirror core's task-data keys exactly in any custom code."],"exampleFix":"// before\nPhabricatorWorker::scheduleTask(\n  'PhabricatorWorkerBulkJobTaskWorker',\n  array('jobID' => $job->getID()));\n\n// after\nPhabricatorWorker::scheduleTask(\n  'PhabricatorWorkerBulkJobTaskWorker',\n  array(\n    'jobID' => $job->getID(),\n    'taskID' => $bulk_task->getID(),\n  ));","handlingStrategy":"validation","validationCode":"if (!idx($task_data, 'taskID')) {\n  throw new InvalidArgumentException(\n    'Bulk task worker data requires a taskID.');\n}\nPhabricatorWorker::scheduleTask($bulk_task_worker_class, $task_data);","typeGuard":"function bulk_task_data_has_task_id($data) {\n  return is_array($data) && !empty($data['taskID']);\n}","tryCatchPattern":null,"preventionTips":["Queue task workers with both 'taskID' and 'jobID' set.","Mirror core's task-data keys exactly in custom schedulers.","Add a scheduling helper so the keys cannot be forgotten."],"tags":["phabricator","bulk-jobs","worker-queue","task-data"],"backgroundTag":"missing-task-data","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}