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

  1. Use the same player Guid returned/used during AddPlayerToGame for every subsequent MakeMove.
  2. Confirm the GameMove.PlayerId is one of the two registered players before submitting.
  3. 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

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


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