phacility/phabricator · error · PhabricatorWorkerPermanentFailureException
Expected task data to be a dictionary.
Error message
Expected task data to be a dictionary.
What it means
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.
Source
Thrown at src/infrastructure/daemon/workers/PhabricatorWorker.php:117
return null;
}
return $task->getID();
}
abstract protected function doWork();
final public function __construct($data) {
$this->data = $data;
}
final protected function getTaskData() {
return $this->data;
}
final protected function getTaskDataValue($key, $default = null) {
$data = $this->getTaskData();
if (!is_array($data)) {
throw new PhabricatorWorkerPermanentFailureException(
pht('Expected task data to be a dictionary.'));
}
return idx($data, $key, $default);
}
final public function executeTask() {
$this->doWork();
}
final public static function scheduleTask(
$task_class,
$data,
$options = array()) {
PhutilTypeSpec::checkMap(
$options,
array(
'priority' => 'optional int|null',View on GitHub (pinned to 5720a38cfe)
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.
Example fix
// before
PhabricatorWorker::scheduleTask(
'MyWorker',
json_encode(array('phid' => $object_phid)));
// after
PhabricatorWorker::scheduleTask(
'MyWorker',
array('phid' => $object_phid)); Defensive patterns
Strategy: type-guard
Validate before calling
if (!is_array($task_data)) {
throw new InvalidArgumentException('Worker task data must be an array.');
}
PhabricatorWorker::scheduleTask($worker_class, $task_data); Type guard
function is_dictionary_task_data($data) {
return is_array($data);
} Try / catch
try {
$value = $this->getTaskDataValue('key');
} catch (PhabricatorWorkerPermanentFailureException $ex) {
// permanent: log the task as garbage; do not requeue blindly
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- No "%s" in task data.
- Task data has no "commitPHID".
- Commit "%s" does not exist.
- Commit "%s" (with PHID "%s") is no longer reachable from any
- Failed to reload commit "%s".
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/0485f583bcfb0cae.
Report an issue: GitHub.