dotnet/orleans · error · ArgumentException
No such playerid for this game
Error message
No such playerid for this game
What it means
Thrown by TicTacToe GameGrain.MakeMove when move.PlayerId is not found in _playerIds (IndexOf returns < 0). It validates that the move came from a player actually seated at this game. The second argument "move" is the legacy string-based paramName form.
Source
Thrown at samples/TicTacToe/Grains/GameGrain.cs:74
// check if the game is ready to play
if (_gameState is GameState.AwaitingPlayers && _playerIds.Count is 2)
{
// a new game is starting
_gameState = GameState.InPlay;
_indexNextPlayerToMove = Random.Shared.Next(0, 1); // random as to who has the first move
}
// let user know if game is ready or not
return Task.FromResult(_gameState);
}
// make a move during the game
public async Task<GameState> MakeMove(GameMove move)
{
// check if its a legal move to make
if (_gameState is not GameState.InPlay) throw new ApplicationException("This game is not in play");
if (_playerIds.IndexOf(move.PlayerId) < 0) throw new ArgumentException("No such playerid for this game", "move");
if (move.PlayerId != _playerIds[_indexNextPlayerToMove]) throw new ArgumentException("The wrong player tried to make a move", "move");
if (move.X < 0 || move.X > 2 || move.Y < 0 || move.Y > 2) throw new ArgumentException("Bad co-ordinates for a move", "move");
if (_board[move.X, move.Y] != -1) throw new ArgumentException("That square is not empty", "move");
// record move
_moves.Add(move);
_board[move.X, move.Y] = _indexNextPlayerToMove;
// check for a winning move
var win = false;
for (var i = 0; i < 3 && !win; i++)
{
win = IsWinningLine(_board[i, 0], _board[i, 1], _board[i, 2]);
}
if (!win)
{View on GitHub (pinned to fca799fa70)
Solutions
- Use the same player Guid returned/used during AddPlayerToGame for every subsequent MakeMove.
- Confirm the GameMove.PlayerId is one of the two registered players before submitting.
- If you need to identify the caller, pass the player id consistently from the client.
Example fix
// before await game.MakeMove(new GameMove(Guid.NewGuid(), x, y)); // new id each call -> throws // after: reuse the registered player id await game.MakeMove(new GameMove(myPlayerId, x, y));
Defensive patterns
Strategy: validation
Validate before calling
// Reuse the registered player id for every move await game.MakeMove(new GameMove(myPlayerId, x, y));
Type guard
// Ensure the move references a known player
static bool IsValidPlayer(GameMove move, IReadOnlyList<Guid> players)
=> players.Contains(move.PlayerId); Try / catch
null
Prevention
- Use the same player Guid from AddPlayerToGame for all moves.
- Do not generate a new player id per call.
- Validate the caller's player id on the client before submitting.
When it happens
Trigger: A client passes a GameMove whose PlayerId does not match either of the two Guids added via AddPlayerToGame. IndexOf(playerId) on _playerIds is negative.
Common situations: Generating a new player Guid per call instead of reusing the one from AddPlayerToGame; cross-talking to the wrong game grain; a client bug that sends another user's id.
Related errors
- Can't join game once its over
- This game is not in play
- Bad co-ordinates for a move
- Missing value for {name}.
- Invalid option {optionId}
AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13).
Data as JSON: /api/errors/a96160e013e67de2.
Report an issue: GitHub.