phacility/phabricator · error · Exception

Option ("%s") is not a valid poll option. You may only vote

Error message

Option ("%s") is not a valid poll option. You may only vote for valid options.

What it means

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.

Source

Thrown at src/applications/slowvote/controller/PhabricatorSlowvoteVoteController.php:60

        $message = pht('You must vote for something.');
      } else {
        $message = pht('You must vote for at least one option.');
      }

      return $this->newDialog()
        ->setTitle(pht('Stand For Something'))
        ->appendParagraph($message)
        ->addCancelButton($poll->getURI());
    }

    if ($is_plurality && count($votes) > 1) {
      throw new Exception(
        pht('In this poll, you may only vote for one option.'));
    }

    foreach ($votes as $vote) {
      if (!isset($options[$vote])) {
        throw new Exception(
          pht(
            'Option ("%s") is not a valid poll option. You may only '.
            'vote for valid options.',
            $vote));
      }
    }

    $poll->openTransaction();
      $poll->beginReadLocking();

      $poll->reload();

      $old_votes = id(new PhabricatorSlowvoteChoice())->loadAllWhere(
        'pollID = %d AND authorPHID = %s',
        $poll->getID(),
        $viewer->getPHID());
      $old_votes = mpull($old_votes, null, 'getOptionID');

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Reload the poll page and vote again so the form carries the current option IDs
  2. If automating, resolve the poll's current option IDs at runtime via PhabricatorSlowvoteOptionQuery with withPollIDs() instead of hardcoding them
  3. Filter submitted vote IDs against the poll's live options before sending the request

Example fix

// before: voting with a hardcoded option id
$votes = array(999);

// after: resolve the poll's current option ids first
$options = id(new PhabricatorSlowvoteOptionQuery())
  ->setViewer($viewer)
  ->withPollIDs(array($poll->getID()))
  ->execute();
$valid = array_fuse(mpull($options, 'getID'));
$votes = array_intersect($votes, $valid);
Defensive patterns

Strategy: validation

Validate before calling

// Before voting, confirm every submitted option id exists on the poll.
$options = id(new PhabricatorSlowvoteOptionQuery())
  ->setViewer($viewer)
  ->withPollIDs(array($poll->getID()))
  ->execute();
$valid = array_fuse(mpull($options, 'getID'));
$votes = array_values(array_intersect($votes, array_keys($valid)));
if (!$votes) {
  // every submitted option is stale; re-render the poll instead of posting
}

Try / catch

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.

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/487dd2141be74bdf. Report an issue: GitHub.