{"record":{"id":"7d9e4e1ed1890bba","repo":"phacility/phabricator","slug":"too-many-relationships-s-of-type-s","errorCode":null,"errorMessage":"Too many relationships (%s, of type \"%s\").","messagePattern":"Too many relationships \\((.+?), of type \"(.+?)\"\\)\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/search/controller/PhabricatorSearchRelationshipController.php","lineNumber":52,"sourceCode":"    $all_phids[] = $src_phid;\n\n    $handles = $viewer->loadHandles($all_phids);\n    $src_handle = $handles[$src_phid];\n\n    $done_uri = $src_handle->getURI();\n    $initial_phids = $dst_phids;\n\n    $maximum = $relationship->getMaximumSelectionSize();\n\n    if ($request->isFormPost()) {\n      $phids = explode(';', $request->getStr('phids'));\n      $phids = array_filter($phids);\n      $phids = array_values($phids);\n\n      // The UI normally enforces this with Javascript, so this is just a\n      // sanity check and does not need to be particularly user-friendly.\n      if ($maximum && (count($phids) > $maximum)) {\n        throw new Exception(\n          pht(\n            'Too many relationships (%s, of type \"%s\").',\n            phutil_count($phids),\n            $relationship->getRelationshipConstant()));\n      }\n\n      $initial_phids = $request->getStrList('initialPHIDs');\n\n      // Apply the changes as adds and removes relative to the original state\n      // of the object when the dialog was rendered so that two users adding\n      // relationships at the same time don't race and overwrite one another.\n      $add_phids = array_diff($phids, $initial_phids);\n      $rem_phids = array_diff($initial_phids, $phids);\n      $all_phids = array_merge($add_phids, $rem_phids);\n\n      $capabilities = $relationship->getRequiredRelationshipCapabilities();\n\n      if ($all_phids) {","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/search/controller/PhabricatorSearchRelationshipController.php#L34-L70","documentation":"PhabricatorSearchRelationshipController processes the POST from the 'add relationships' dialog (subscribers, projects, depends on, etc.). The UI normally caps selection in Javascript; server-side it re-checks count($phids) against the relationship's getMaximumSelectionSize() (e.g. 1 for 'maniphest.subscribers'? actually 1 for owner-type relationships) and throws this raw Exception as a sanity check when the POST carries more PHIDs than allowed. It is intentionally not user-friendly because a legit client never triggers it.","triggerScenarios":"POSTing to /search/relationship/... with a 'phids' semicolon-separated string containing more entries than the relationship allows - e.g. assigning two owners to a task via a crafted request, or a stale browser tab using an old dialog that did not enforce the cap.","commonSituations":"Custom scripts or API clients driving the relationship dialog endpoint directly with unclipped lists; browser extension or userscript modifying the tokenizer; multiple-SELECT UI bug after an upgrade that removed the JS cap; race where a user pastes a large list into a single-select dialog.","solutions":["If this fires in normal use, report it as a Phabricator UI bug - the Javascript tokenizer should have limited selection before POST.","If you drive this endpoint programmatically, slice the PHID list to the relationship maximum before POSTing (query the relationship config for the cap).","Refresh the dialog (reload the page) so the current, cap-enforcing Javascript is loaded instead of a stale cached one.","Split bulk additions into multiple requests that each respect the cap."],"exampleFix":"// before\n$phids = explode(';', $request->getStr('phids'));\n\n// after (client side: cap before submit)\nvar phids = tokens.map(t => t.getPHID());\nif (phids.length > config.getMaximumSelectionSize()) {\n  phids = phids.slice(0, config.getMaximumSelectionSize());\n}","handlingStrategy":"validation","validationCode":"// Client side: cap selection before POST\nvar max = config.maximumSelectionSize;\nif (max && tokens.length > max) { tokens = tokens.slice(0, max); }\n\n// API side: know the cap before building the request\n$max = $relationship->getMaximumSelectionSize();\n$phids = $max ? array_slice($phids, 0, $max) : $phids;","typeGuard":null,"tryCatchPattern":"// Server code already wraps this into a dialog; callers should not hit it.\n// If scripting the endpoint, validate first as above and treat any Exception\n// here as a bug report, not a recoverable condition.","preventionTips":["Always drive relationship dialogs through the shipped Javascript tokenizer","Re-check caps after Phabricator upgrades change relationship limits","Never craft raw POSTs to /search/relationship/ with unbounded phids lists"],"tags":["phabricator","relationships","form-validation","limit-exceeded","controller"],"backgroundTag":"selection-limit-exceeded","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}