{"record":{"id":"487dd2141be74bdf","repo":"phacility/phabricator","slug":"option-s-is-not-a-valid-poll-option-you-may","errorCode":null,"errorMessage":"Option (\"%s\") is not a valid poll option. You may only vote for valid options.","messagePattern":"Option \\(\"(.+?)\"\\) is not a valid poll option\\. You may only vote for valid options\\.","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/slowvote/controller/PhabricatorSlowvoteVoteController.php","lineNumber":60,"sourceCode":"        $message = pht('You must vote for something.');\n      } else {\n        $message = pht('You must vote for at least one option.');\n      }\n\n      return $this->newDialog()\n        ->setTitle(pht('Stand For Something'))\n        ->appendParagraph($message)\n        ->addCancelButton($poll->getURI());\n    }\n\n    if ($is_plurality && count($votes) > 1) {\n      throw new Exception(\n        pht('In this poll, you may only vote for one option.'));\n    }\n\n    foreach ($votes as $vote) {\n      if (!isset($options[$vote])) {\n        throw new Exception(\n          pht(\n            'Option (\"%s\") is not a valid poll option. You may only '.\n            'vote for valid options.',\n            $vote));\n      }\n    }\n\n    $poll->openTransaction();\n      $poll->beginReadLocking();\n\n      $poll->reload();\n\n      $old_votes = id(new PhabricatorSlowvoteChoice())->loadAllWhere(\n        'pollID = %d AND authorPHID = %s',\n        $poll->getID(),\n        $viewer->getPHID());\n      $old_votes = mpull($old_votes, null, 'getOptionID');\n","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/slowvote/controller/PhabricatorSlowvoteVoteController.php#L42-L78","documentation":"Thrown by PhabricatorSlowvoteVoteController when processing a ballot: each submitted option ID is checked against the poll's current option rows (the $options map). A vote value that is not a key in that set is rejected before any transaction or read lock is taken. It is server-side validation that the submitted ballot matches the poll as it currently exists in the database.","triggerScenarios":"POSTing a vote to the Slowvote controller with votes[] containing an option ID that has no PhabricatorSlowvoteOption row for that poll: a stale form rendered before the poll's options changed, a forged or hand-crafted HTTP request, or a race between page render and submit.","commonSituations":"Two browser tabs open on the same poll, one stale after options were edited; automation voting with hardcoded option IDs after the poll was recreated; a browser resubmitting a cached form.","solutions":["Reload the poll page and vote again so the form carries the current option IDs","If automating, resolve the poll's current option IDs at runtime via PhabricatorSlowvoteOptionQuery with withPollIDs() instead of hardcoding them","Filter submitted vote IDs against the poll's live options before sending the request"],"exampleFix":"// before: voting with a hardcoded option id\n$votes = array(999);\n\n// after: resolve the poll's current option ids first\n$options = id(new PhabricatorSlowvoteOptionQuery())\n  ->setViewer($viewer)\n  ->withPollIDs(array($poll->getID()))\n  ->execute();\n$valid = array_fuse(mpull($options, 'getID'));\n$votes = array_intersect($votes, $valid);","handlingStrategy":"validation","validationCode":"// Before voting, confirm every submitted option id exists on the poll.\n$options = id(new PhabricatorSlowvoteOptionQuery())\n  ->setViewer($viewer)\n  ->withPollIDs(array($poll->getID()))\n  ->execute();\n$valid = array_fuse(mpull($options, 'getID'));\n$votes = array_values(array_intersect($votes, array_keys($valid)));\nif (!$votes) {\n  // every submitted option is stale; re-render the poll instead of posting\n}","typeGuard":null,"tryCatchPattern":"Catch the plain Exception in the controller/HTTP layer and re-render the poll with a 'poll has changed, reload' dialog (the controller already builds AphrontDialogResponse for sibling failures like plurality violations); never retry the identical payload.","preventionTips":["Submit votes only from a freshly rendered poll form","Resolve option IDs from the poll at runtime instead of caching them","Treat this exception as a permanent condition for the submitted ballot; fix the input, do not retry"],"tags":["slowvote","polls","form-validation","user-input"],"backgroundTag":"invalid-form-value","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}