dotnet/orleans · error · KeyNotFoundException
Invalid option {optionId}
Error message
Invalid option {optionId} What it means
Thrown by Voting PollGrain.AddVote when optionId is outside the valid index range of the poll's Options list (optionId < 0 || optionId >= options.Count). KeyNotFoundException signals that the requested vote option does not exist on this poll.
Source
Thrown at samples/Voting/Grains/PollGrain.cs:30
[PersistentState(stateName: "pollState", storageName: "votes")]
IPersistentState<PollState> state) => _votes = state;
public Task<PollState> GetCurrentResults() => Task.FromResult(_votes.State);
public async Task CreatePoll(PollState initialState)
{
// Set the state and persist it
_votes.State = initialState;
await _votes.WriteStateAsync();
}
public async Task<PollState> AddVote(int optionId)
{
// Perform input validation
var options = _votes.State.Options;
if (optionId < 0 || optionId >= options.Count)
{
throw new KeyNotFoundException($"Invalid option {optionId}");
}
// Add the vote & persist the updated state.
var (option, votes) = options[optionId];
options[optionId] = (option, votes + 1);
await _votes.WriteStateAsync();
// Notify the watchers.
_pollWatchers.Notify(watcher => watcher.OnPollUpdated(_votes.State));
return _votes.State;
}
private readonly ObserverManager<IPollWatcher> _pollWatchers = new(TimeSpan.FromMinutes(1));
public Task StartWatching(IPollWatcher watcher)
{
_pollWatchers.Subscribe(watcher);
return Task.CompletedTask;View on GitHub (pinned to fca799fa70)
Solutions
- Fetch the current PollState (GetCurrentResults) and use its Options.Count to bound optionId before voting.
- Validate optionId >= 0 && optionId < options.Length on the client.
- Catch KeyNotFoundException at the caller and present 'option no longer available' to the user.
Example fix
// before await poll.AddVote(optionId); // throws if out of range // after: validate against the live poll definition var state = await poll.GetCurrentResults(); if (optionId < 0 || optionId >= state.Options.Count) return; await poll.AddVote(optionId);
Defensive patterns
Strategy: validation
Validate before calling
// Validate the option id against the current poll before voting var state = await poll.GetCurrentResults(); if (optionId < 0 || optionId >= state.Options.Count) return; await poll.AddVote(optionId);
Type guard
static bool IsValidOption(int optionId, PollState state)
=> optionId >= 0 && optionId < state.Options.Count; Try / catch
null
Prevention
- Fetch the live PollState and bound optionId against Options.Count.
- Do not hard-code option ids that may drift across poll re-creation.
- Catch KeyNotFoundException to show 'option unavailable' to users.
When it happens
Trigger: A client calls pollGrain.AddVote(optionId) with an optionId that is negative or >= the number of options configured when the poll was created.
Common situations: Hard-coding an option id that drifts after the poll is re-created; a client reading a stale poll definition; off-by-one when the client assumes a specific option ordering.
Related errors
- You have already created 5 polls, which is enough for anybod
- You have already voted in that poll!
- The requested vote option was not found.
- Missing value for {name}.
- Can't join game once its over
AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13).
Data as JSON: /api/errors/35da3bb96d8dbcc9.
Report an issue: GitHub.