{"record":{"id":"5625c4caeb02e31b","repo":"dotnet/orleans","slug":"you-have-already-voted-in-that-poll","errorCode":null,"errorMessage":"You have already voted in that poll!","messagePattern":"You have already voted in that poll!","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"samples/Voting/Grains/UserAgentGrain.cs","lineNumber":70,"sourceCode":"        var pollId = Guid.NewGuid().ToString(\"N\")[..6];\n        var pollGrain = _grainFactory.GetGrain<IPollGrain>(pollId);\n\n        // Create the poll. We could avoid collisions here by making this return an error if a poll\n        // with that id already exists.\n        await pollGrain.CreatePoll(initialState);\n\n        _myPolls.Add(pollId);\n        return pollId;\n    }\n\n    public async Task<PollState> AddVote(string pollId, int optionId)\n    {\n        Throttle();\n\n        // First, check to see whether we've voted already.\n        if (_votedPolls.Contains(pollId))\n        {\n            throw new InvalidOperationException(\"You have already voted in that poll!\");\n        }\n\n        // Vote\n        var pollGrain = _grainFactory.GetGrain<IPollGrain>(pollId);\n        var result = await pollGrain.AddVote(optionId);\n\n        // Record our vote to prevent double-voting.\n        _votedPolls.Add(pollId);\n        return result;\n    }\n\n    private void Throttle()\n    {\n        // Work out how long it's been since the last call.\n        var elapsedSeconds = _stopwatch.Elapsed.TotalSeconds;\n        _stopwatch.Restart();\n\n        // Calculate a new score based on a constant rate of score decay and the","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/dotnet/orleans/blob/fca799fa70ecb6ad975224271703ca43221f58de/samples/Voting/Grains/UserAgentGrain.cs#L52-L88","documentation":"Thrown by Voting UserAgentGrain.AddVote when the user has already voted in the given poll (_votedPolls.Contains(pollId)). The user agent records each poll a user has voted in to prevent double-voting at the per-user level (in addition to whatever the PollGrain enforces).","triggerScenarios":"The same user calls AddVote(pollId, optionId) twice for the same pollId; the second call finds pollId already in the _votedPolls set.","commonSituations":"A client retrying a vote; a UI that allows clicking vote again; the user genuinely trying to change their vote (not supported by this sample).","solutions":["Do not re-vote in the same poll; the sample does not support changing a vote.","If changing votes is a requirement, remove the pollId from _votedPolls (and decrement the prior option) before allowing a new vote.","Catch InvalidOperationException client-side and inform the user they already voted."],"exampleFix":"// before\nawait user.AddVote(pollId, optionId); // throws the second time\n\n// after: check before voting\nif (await user.HasVoted(pollId)) { ShowMessage(\"Already voted.\"); return; }\nawait user.AddVote(pollId, optionId);","handlingStrategy":"validation","validationCode":"// Check the user's voted set before voting\nif (await user.HasVoted(pollId)) { ShowMessage(\"Already voted.\"); return; }\nawait user.AddVote(pollId, optionId);","typeGuard":"null","tryCatchPattern":"try { await user.AddVote(pollId, optionId); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"already voted\"))\n{ /* user already voted in this poll */ }","preventionTips":["Do not retry votes for the same poll.","Track voted polls in the client and disable the control.","If vote-changing is required, modify the sample to clear _votedPolls first."],"tags":["voting","validation","duplicate","sample"],"backgroundTag":null,"analyzedSha":"fca799fa70ecb6ad975224271703ca43221f58de","analyzedAt":"2026-08-13T19:55:57.938Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}