{"record":{"id":"0485f583bcfb0cae","repo":"phacility/phabricator","slug":"expected-task-data-to-be-a-dictionary","errorCode":null,"errorMessage":"Expected task data to be a dictionary.","messagePattern":"Expected task data to be a dictionary\\.","errorType":"exception","errorClass":"PhabricatorWorkerPermanentFailureException","httpStatus":null,"severity":"error","filePath":"src/infrastructure/daemon/workers/PhabricatorWorker.php","lineNumber":117,"sourceCode":"      return null;\n    }\n    return $task->getID();\n  }\n\n  abstract protected function doWork();\n\n  final public function __construct($data) {\n    $this->data = $data;\n  }\n\n  final protected function getTaskData() {\n    return $this->data;\n  }\n\n  final protected function getTaskDataValue($key, $default = null) {\n    $data = $this->getTaskData();\n    if (!is_array($data)) {\n      throw new PhabricatorWorkerPermanentFailureException(\n        pht('Expected task data to be a dictionary.'));\n    }\n    return idx($data, $key, $default);\n  }\n\n  final public function executeTask() {\n    $this->doWork();\n  }\n\n  final public static function scheduleTask(\n    $task_class,\n    $data,\n    $options = array()) {\n\n    PhutilTypeSpec::checkMap(\n      $options,\n      array(\n        'priority' => 'optional int|null',","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/infrastructure/daemon/workers/PhabricatorWorker.php#L99-L135","documentation":"Worker task data is whatever was passed to PhabricatorWorker::scheduleTask(); getTaskDataValue() assumes it is an associative array and throws PhabricatorWorkerPermanentFailureException when it is not. Permanent failure marks the task as garbage instead of retrying it. In practice the scheduler passed a JSON string (or a scalar) where a plain PHP array was expected.","triggerScenarios":"Calling PhabricatorWorker::scheduleTask('MyWorker', json_encode(array(...))) and then having MyWorker call getTaskDataValue(); scheduling with null or integer payloads; legacy task rows queued before a data-format convention was adopted.","commonSituations":"New worker code that 'helpfully' pre-encodes the payload to JSON; external systems inserting raw string payloads into the worker table; refactors that changed what scheduleTask receives.","solutions":["Change the scheduler to pass a plain PHP array to scheduleTask().","If payloads may arrive pre-encoded, decode in the worker before reading keys: $data = $this->getTaskData(); if (is_string($data)) { $data = phutil_json_decode($data); }.","Inspect the failed task's stored data to confirm what was actually queued."],"exampleFix":"// before\nPhabricatorWorker::scheduleTask(\n  'MyWorker',\n  json_encode(array('phid' => $object_phid)));\n\n// after\nPhabricatorWorker::scheduleTask(\n  'MyWorker',\n  array('phid' => $object_phid));","handlingStrategy":"type-guard","validationCode":"if (!is_array($task_data)) {\n  throw new InvalidArgumentException('Worker task data must be an array.');\n}\nPhabricatorWorker::scheduleTask($worker_class, $task_data);","typeGuard":"function is_dictionary_task_data($data) {\n  return is_array($data);\n}","tryCatchPattern":"try {\n  $value = $this->getTaskDataValue('key');\n} catch (PhabricatorWorkerPermanentFailureException $ex) {\n  // permanent: log the task as garbage; do not requeue blindly\n}","preventionTips":["Never json_encode payloads for scheduleTask(); pass plain arrays.","Normalize in doWork() first: $data = $this->getTaskData(); if (is_string($data)) { $data = phutil_json_decode($data); }.","Audit any producer that inserts into the worker table directly."],"tags":["phabricator","worker-queue","task-data","php"],"backgroundTag":"invalid-task-payload","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}