dotnet/orleans · error · ArgumentException

Bad co-ordinates for a move

Error message

Bad co-ordinates for a move

What it means

Thrown by TicTacToe GameGrain.MakeMove when move.X or move.Y is outside the 0..2 range (the board is a 3x3 grid). It guards against index-out-of-range access into the _board array before the move is recorded.

Source

Thrown at samples/TicTacToe/Grains/GameGrain.cs:77

            // 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)
        {
            for (var i = 0; i < 3 && !win; i++)
            {
                win = IsWinningLine(_board[0, i], _board[1, i], _board[2, i]);

View on GitHub (pinned to fca799fa70)

Solutions

  1. Clamp/validate coordinates client-side to 0,1,2 before calling MakeMove.
  2. If your UI is one-indexed, subtract one before constructing GameMove.
  3. Catch ArgumentException at the call site and surface a user-facing message.

Example fix

// before
await game.MakeMove(new GameMove(playerId, x, y)); // throws if x or y is 3

// after: validate on the client
if (x is < 0 or > 2 || y is < 0 or > 2) return;
await game.MakeMove(new GameMove(playerId, x, y));
Defensive patterns

Strategy: validation

Validate before calling

// Clamp coordinates to the 3x3 board
if (move.X is < 0 or > 2 || move.Y is < 0 or > 2) return;
await game.MakeMove(move);

Type guard

static bool IsValidMove(GameMove move)
    => move.X is >= 0 and <= 2 && move.Y is >= 0 and <= 2;

Try / catch

null

Prevention

When it happens

Trigger: A client submits a GameMove with X or Y less than 0 or greater than 2. The bounds check `move.X < 0 || move.X > 2 || move.Y < 0 || move.Y > 2` fires.

Common situations: A client UI letting the user click outside the 3x3 grid; off-by-one or one-indexed coordinates from the client; deserializing a malformed move.

Related errors


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