dotnet/orleans · error · KeyNotFoundException

The requested vote option was not found.

Error message

The requested vote option was not found.

What it means

Thrown by Voting VoteGrain.RemoveVote when the lower-cased option key is not present in the grain's persisted state dictionary. KeyNotFoundException indicates the vote option you tried to delete does not exist (it was never created or already removed).

Source

Thrown at samples/Voting/Grains/VoteGrain.cs:55

        {
            _logger.LogInformation("Recorded a vote for an existing option");
            _state.State[key] += 1;
        }

        await _state.WriteStateAsync();
        _logger.LogInformation("Saved vote in {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
    }

    public async Task RemoveVote(string option)
    {
        var stopwatch = Stopwatch.StartNew();
        _logger.LogInformation("Deleting vote option");

        var key = option.ToLower();
        if (!_state.State.ContainsKey(key))
        {
            _logger.LogWarning("Didn't find the requested vote option");
            throw new KeyNotFoundException("The requested vote option was not found.");
        }
        else
        {
            _logger.LogInformation("Removed a vote option");
            _state.State.Remove(key.ToLower());
        }

        await _state.WriteStateAsync();

        _logger.LogInformation("Deleted vote option {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
    }
}

View on GitHub (pinned to fca799fa70)

Solutions

  1. Check that the option exists before removing, or treat its absence as success (make RemoveVote idempotent).
  2. Avoid calling RemoveVote twice for the same option.
  3. Trim and normalize the option string on the client before sending.

Example fix

// before
public async Task RemoveVote(string option) {
  if (!_state.State.ContainsKey(key)) throw new KeyNotFoundException("...");
  _state.State.Remove(key);
}

// after: idempotent remove
public async Task RemoveVote(string option) {
  _state.State.Remove(option.ToLower());
}
Defensive patterns

Strategy: validation

Validate before calling

// Normalize and check existence before removing
var key = option.Trim().ToLower();
if (!await vote.HasOption(key)) return;
await vote.RemoveVote(option);

Type guard

null

Try / catch

try { await vote.RemoveVote(option); }
catch (KeyNotFoundException) { /* already absent; treat as success */ }

Prevention

When it happens

Trigger: A client calls voteGrain.RemoveVote(option) with an option string whose lower-cased form is not a key in _state.State.

Common situations: Calling RemoveVote before the option was added; calling it twice (the second time the key is gone); case mismatch where the stored key has different casing — note the grain lower-cases both stored and queried keys, so pure case alone should not trigger it, but a leading/trailing space would.

Related errors


AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13). Data as JSON: /api/errors/c212a816e4f4dfb1. Report an issue: GitHub.