phacility/phabricator · error · Exception
Add project rule value should be a list, but is not
Error message
Add project rule value should be a list, but is not (value is "%s").
What it means
Thrown by assertValidRuleRecordFormat() when the stored value of a 'task.projects.add' (Add Projects) trigger rule is not a PHP array. The check runs in PhabricatorProjectTriggerRule::setRecord(), which executes every time a trigger rule record is loaded from the database (workboard rendering, trigger editing, card drop). It means the JSON in PhabricatorProjectTriggerRuleRecord.value is corrupt or was written with the wrong shape.
Solutions
- Inspect the row: SELECT * FROM phabricator_project_trigger_rule_record WHERE type='task.projects.add' and confirm the value column holds a JSON list.
- Fix the stored value to a JSON array of project PHIDs, e.g. ["PHID-PROJ-xyz"], via the trigger editor (preferred) or a storage repair script.
- If the record came from an import/migration, correct the writer so it always emits an array for tokenizer-backed rules.
- After repair, reload the workboard or trigger edit page to confirm setRecord() no longer throws.
Example fix
// before: trigger rule record value (corrupt)
$value = 'PHID-PROJ-xyz';
// after: list of project PHIDs
$value = array('PHID-PROJ-xyz'); Defensive patterns
Strategy: type-guard
Validate before calling
// Before writing a task.projects.add rule record value:
if (!is_array($value)) {
throw new InvalidArgumentException('add-projects value must be a list of project PHIDs');
}
PhutilTypeSpec::checkMap... // or simply:
assert(is_array($value)); Type guard
function isAddProjectsRuleValue($value) {
return is_array($value) && !empty($value)
&& preg_match('/^PHID-PROJ-/', implode(',', $value));
} Try / catch
try {
$rule->setRecord($record);
} catch (Exception $ex) {
// Corrupt stored trigger: log record ID, quarantine rule, keep board usable
phlog($ex);
$rule = null;
} Prevention
- Always create trigger records through the trigger editor or the standard editor APIs so value shapes match the rule type.
- Validate value shape (is_array for list rules) before persisting PhabricatorProjectTriggerRuleRecord rows.
- Back up and dry-run any script that bulk-edits phabricator_project_trigger_rule_record.
When it happens
Trigger: Loading or saving any trigger whose rule of type 'task.projects.add' has a non-list value, e.g. the value column contains a bare PHID string instead of a JSON array of PHIDs. Reproduce with: a trigger record where phabricator_project_trigger_rule_record.value = "PHID-PROJ-xyz" instead of ["PHID-PROJ-xyz"], then open a workboard that uses the trigger.
Common situations: Hand-edited database rows, a migration or import script that wrote scalar values, an older Phorge/Phabricator version that stored the value differently, or a custom code path calling setRecord() with a hand-built record object.
Related errors
- Owner rule value should be a list, but is not
- Priority rule value should be a string, but is not
- Remove project rule value should be a list, but is not
- Status rule value should be a string, but is not
- Status rule value should be a string, but is not
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/645ae956605f57e1.
Report an issue: GitHub.
Appendix: source
Thrown at src/applications/project/trigger/PhabricatorProjectTriggerAddProjectsRule.php:18
<?php
final class PhabricatorProjectTriggerAddProjectsRule
extends PhabricatorProjectTriggerRule {
const TRIGGERTYPE = 'task.projects.add';
public function getSelectControlName() {
return pht('Add project tags');
}
protected function getValueForEditorField() {
return $this->getDatasource()->getWireTokens($this->getValue());
}
protected function assertValidRuleRecordFormat($value) {
if (!is_array($value)) {
throw new Exception(
pht(
'Add project rule value should be a list, but is not '.
'(value is "%s").',
phutil_describe_type($value)));
}
}
protected function assertValidRuleRecordValue($value) {
if (!$value) {
throw new Exception(
pht(
'You must select at least one project tag to add.'));
}
}
protected function newDropTransactions($object, $value) {
$project_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
View on GitHub (pinned to 5720a38cfe)