{"record":{"id":"933a44ca347139a0","repo":"phacility/phabricator","slug":"transaction-specifies-both-afterphid-and-afterp","errorCode":null,"errorMessage":"Transaction specifies both \"afterPHID\" and \"afterPHIDs\". Specify only \"afterPHIDs\".","messagePattern":"Transaction specifies both \"afterPHID\" and \"afterPHIDs\"\\. Specify only \"afterPHIDs\"\\.","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/maniphest/editor/ManiphestTransactionEditor.php","lineNumber":479,"sourceCode":"          'columnPHID' => 'string',\n          'beforePHIDs' => 'optional list<string>',\n          'afterPHIDs' => 'optional list<string>',\n\n          // Deprecated older variations of \"beforePHIDs\" and \"afterPHIDs\".\n          'beforePHID' => 'optional string',\n          'afterPHID' => 'optional string',\n        ));\n\n      $value = $value + array(\n        'beforePHIDs' => array(),\n        'afterPHIDs' => array(),\n      );\n\n      // Normalize the legacy keys \"beforePHID\" and \"afterPHID\" keys to the\n      // modern format.\n      if (!empty($value['afterPHID'])) {\n        if ($value['afterPHIDs']) {\n          throw new Exception(\n            pht(\n              'Transaction specifies both \"afterPHID\" and \"afterPHIDs\". '.\n              'Specify only \"afterPHIDs\".'));\n        }\n        $value['afterPHIDs'] = array($value['afterPHID']);\n        unset($value['afterPHID']);\n      }\n\n      if (isset($value['beforePHID'])) {\n        if ($value['beforePHIDs']) {\n          throw new Exception(\n            pht(\n              'Transaction specifies both \"beforePHID\" and \"beforePHIDs\". '.\n              'Specify only \"beforePHIDs\".'));\n        }\n        $value['beforePHIDs'] = array($value['beforePHID']);\n        unset($value['beforePHID']);\n      }","sourceCodeStart":461,"sourceCodeEnd":497,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/maniphest/editor/ManiphestTransactionEditor.php#L461-L497","documentation":"When a Maniphest move/column transaction positions a task relative to other tasks on a workboard column, the value may use the modern 'afterPHIDs' (list) or the deprecated legacy 'afterPHID' (single string) — but not both. buildMoveTransaction normalizes the legacy key and throws this Exception if the modern list is non-empty at the same time.","triggerScenarios":"A column move value like {\"columnPHID\": \"PHID-PCOL-...\", \"afterPHID\": \"PHID-TASK-aaa\", \"afterPHIDs\": [\"PHID-TASK-bbb\"]} sent via maniphest.edit 'core:move' / column transactions or built by custom drag-and-drop glue code that merged two payload formats.","commonSituations":"Client code updated from the old single-PHID format to the list format while a legacy branch still adds 'afterPHID'. Merging default payloads with user-specified ordering parameters. Third-party workboard automation written against documentation from different eras.","solutions":["Send only the modern list key: \"afterPHIDs\": [\"PHID-TASK-aaa\"] and drop 'afterPHID' entirely.","Audit payload-merging code (array union / array_merge of defaults with caller input) so the legacy key cannot survive when the list is set.","Note the same rule applies to 'beforePHID' vs 'beforePHIDs' on the same transaction."],"exampleFix":"// before\n$value = array(\n  'columnPHID' => $column_phid,\n  'afterPHID'  => $after_phid,      // legacy\n  'afterPHIDs' => array($after_phid), // both -> throws\n);\n\n// after\n$value = array(\n  'columnPHID' => $column_phid,\n  'afterPHIDs' => array($after_phid),\n);","handlingStrategy":"validation","validationCode":"// Normalize before sending a move/column transaction\nif (!empty($move['afterPHID'])) {\n  if (!empty($move['afterPHIDs'])) {\n    unset($move['afterPHID']); // or reject: conflicting keys\n  } else {\n    $move['afterPHIDs'] = array($move['afterPHID']);\n    unset($move['afterPHID']);\n  }\n}","typeGuard":"function uses_modern_position_keys(array $move) {\n  return empty($move['afterPHID']) && empty($move['beforePHID'])\n    && empty($move['afterPHIDs']) === false || true; // ensure no legacy keys present\n}","tryCatchPattern":null,"preventionTips":["Send only 'beforePHIDs'/'afterPHIDs' lists in new code.","Never merge legacy single-PHID payloads with modern list payloads.","Unit-test payload builders to assert legacy keys are absent."],"tags":["phabricator","maniphest","workboard","transaction","legacy-key-conflict"],"backgroundTag":"conflicting-format-keys","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}