Troubleshooting
Common issues and solutions when using the Chess Game SDK
Troubleshooting
This guide covers common issues you might encounter when integrating the Chess Game SDK and their solutions.
Common Error Codes
Game State Errors
GameNotWaiting (6003)
Error: Game is not in waiting state
Cause: Trying to join a game that has already started or finished
Solution:
// Check game status before joining
const gameAccount = await fetchGameAccount(rpc, gameAccountPDA);
if (gameAccount.data.gameStatus !== GameStatus.WaitingForPlayer) {
console.log("Game is not available for joining");
return;
}GameNotActive (6005)
Error: Game is not active
Cause: Trying to make moves in a game that hasn't started or has finished
Solution:
// Verify game is in progress
if (gameData.gameStatus !== GameStatus.InProgress) {
throw new Error("Cannot make moves - game is not active");
}GameAlreadyFinished (6008)
Error: Game already finished
Cause: Attempting actions on a completed game
Solution:
// Check if game is still ongoing
if (gameData.gameStatus === GameStatus.Finished) {
console.log("Game has already ended");
// Redirect to game results or claim winnings
return;
}Player and Turn Errors
NotPlayerTurn (6006)
Error: Not player's turn
Cause: Player trying to move when it's opponent's turn
Solution:
// Validate turn before making move
const isPlayerTurn = (
(gameData.currentTurn === PieceColor.White && player.equals(gameData.whitePlayer)) ||
(gameData.currentTurn === PieceColor.Black && player.equals(gameData.blackPlayer))
);
if (!isPlayerTurn) {
console.log("Wait for your turn");
return;
}NotGamePlayer (6004)
Error: Player is not part of this game
Cause: Non-participant trying to interact with the game
Solution:
// Verify player participation
const isParticipant = (
player.equals(gameData.whitePlayer) ||
player.equals(gameData.blackPlayer)
);
if (!isParticipant) {
throw new Error("You are not a player in this game");
}CannotJoinOwnGame (6015)
Error: Cannot join your own game
Cause: Game creator trying to join their own game
Solution:
// Check if player is the creator
if (player.equals(gameData.whitePlayer)) {
console.log("Cannot join your own game");
return;
}Move Validation Errors
InvalidMove (6007)
Error: Invalid move
Cause: Move violates chess rules or game constraints
Solution:
import { ChessUtils } from "@sendarcade/checkmate";
const utils = new ChessUtils();
// Validate move before sending transaction
const isValidMove = utils.isValidMove(
gameData.board,
fromSquare,
toSquare,
gameData.currentTurn
);
if (!isValidMove) {
console.log("Invalid move - please try again");
return;
}MoveExposesKing (6012)
Error: Move would leave king in check
Cause: Attempted move would put own king in check
Solution:
// Check if move leaves king in check
const wouldExposeKing = utils.wouldExposeKing(
gameData.board,
fromSquare,
toSquare,
gameData.currentTurn
);
if (wouldExposeKing) {
console.log("Cannot make move - would expose king to check");
return;
}NoPieceAtSquare (6017)
Error: No piece at source square
Cause: Trying to move from an empty square
Solution:
// Verify piece exists at source square
const piece = utils.getPieceAt(gameData.board, fromSquare);
if (!piece) {
console.log("No piece at selected square");
return;
}NotYourPiece (6018)
Error: Not your piece
Cause: Trying to move opponent's piece
Solution:
// Check piece ownership
const piece = utils.getPieceAt(gameData.board, fromSquare);
if (piece && piece.color !== gameData.currentTurn) {
console.log("Cannot move opponent's piece");
return;
}Economic Errors
InsufficientFunds (6002)
Error: Insufficient funds to create game
Cause: Player doesn't have enough tokens for entry fee
Solution:
// Check token balance before creating game
const tokenAccount = await getAssociatedTokenAddress(
tokenMint,
player.publicKey
);
const balance = await connection.getTokenAccountBalance(tokenAccount);
if (balance.value.uiAmount < entryFeeAmount) {
console.log("Insufficient funds for entry fee");
return;
}EntryFeeTooHigh (6001)
Error: Entry fee is too high
Cause: Entry fee exceeds platform limits
Solution:
// Validate entry fee against platform limits
const MAX_ENTRY_FEE = 1000 * 1e6; // 1000 USDC
if (entryFee > MAX_ENTRY_FEE) {
console.log("Entry fee exceeds maximum allowed");
return;
}AlreadyClaimed (6025)
Error: Player has already claimed winnings
Cause: Attempting to claim winnings multiple times
Solution:
// Check if winnings already claimed
if (gameData.winningsClaimed) {
console.log("Winnings have already been claimed");
return;
}Draw System Errors
DrawOffersNotAllowed (6026)
Error: Draw offers are not allowed in this game
Cause: Game was created with draw offers disabled
Solution:
// Check if draw offers are allowed
if (!gameData.allowDrawOffers) {
console.log("Draw offers are not allowed in this game");
return;
}NoDrawOfferPending (6029)
Error: No draw offer pending
Cause: Trying to accept/reject when no draw offer exists
Solution:
// Check for pending draw offer
if (!gameData.drawOffer.pending) {
console.log("No draw offer to respond to");
return;
}Network and RPC Issues
Magicblock Ephemeral Rollup
Issue: Cannot fetch game data during active gameplay
Cause: Game is delegated to Ephemeral Rollup but using wrong RPC
Solution:
// Use appropriate RPC based on game status
const getGameData = async (gameAccountPDA: Address) => {
try {
// Try standard RPC first
const gameAccount = await fetchGameAccount(rpc, gameAccountPDA);
if (gameAccount.exists && gameAccount.data.gameStatus === GameStatus.InProgress) {
// Game is in Ephemeral Rollup, use ER RPC
return await fetchGameAccount(erRpc, gameAccountPDA);
}
return gameAccount;
} catch (error) {
console.error("Failed to fetch game data:", error);
throw error;
}
};Transaction Failures
Issue: Transactions failing with "Transaction simulation failed"
Cause: Various reasons including insufficient funds, invalid state, or network issues
Solution:
const buildAndSendTransaction = async (
rpc: Rpc,
instructions: TransactionInstruction[],
signer: TransactionSigner
) => {
try {
// Build transaction with recent blockhash
const { blockhash } = await rpc.getLatestBlockhash().send();
const transaction = pipe(
createTransaction({ version: 0 }),
(tx) => setTransactionFeePayer(signer.address, tx),
(tx) => setTransactionLifetimeUsingBlockhash(blockhash, tx),
(tx) => appendTransactionInstructions(instructions, tx)
);
// Sign and send
const signedTransaction = await signTransaction([signer], transaction);
const signature = await rpc.sendTransaction(signedTransaction).send();
// Wait for confirmation
await rpc.confirmTransaction(signature, { commitment: "confirmed" }).send();
return signature;
} catch (error) {
console.error("Transaction failed:", error);
// Retry logic for network issues
if (error.message.includes("blockhash not found")) {
console.log("Retrying with fresh blockhash...");
// Implement retry logic
}
throw error;
}
};SDK Integration Issues
Import Errors
Issue: Cannot import SDK modules
Solution:
// Correct import syntax
import { ChessGameSDK, ChessUtils } from "@sendarcade/checkmate";
import { address, Rpc } from "@solana/kit";
// For CommonJS environments
const { ChessGameSDK } = require("@sendarcade/checkmate");Type Errors
Issue: TypeScript compilation errors
Solution:
// Ensure proper type imports
import type {
GameAccount,
PieceColor,
GameStatus,
TransactionSigner
} from "@sendarcade/checkmate";
// Use proper type assertions
const gameId = BigInt(1); // Not just 1
const integratorId = address("YourIntegratorId"); // Use address() helperDebugging Strategies
Enable Detailed Logging
// Add comprehensive logging
const makeMove = async (fromSquare: string, toSquare: string) => {
console.log(`Attempting move: ${fromSquare} -> ${toSquare}`);
console.log(`Current turn: ${gameData.currentTurn}`);
console.log(`Player: ${player.toString()}`);
try {
const { instruction } = await chessSDK.makeMoveIx({
player,
gameId,
integratorId,
fromSquare,
toSquare
});
console.log("Instruction created successfully");
const signature = await buildAndSendTransaction(rpc, [instruction], signer);
console.log(`Move successful: ${signature}`);
} catch (error) {
console.error("Move failed:", {
error: error.message,
fromSquare,
toSquare,
gameId: gameId.toString(),
player: player.toString()
});
throw error;
}
};Game State Validation
// Comprehensive game state checker
const validateGameState = (gameData: GameAccount) => {
const issues = [];
if (!gameData.whitePlayer) {
issues.push("Missing white player");
}
if (!gameData.blackPlayer && gameData.gameStatus !== GameStatus.WaitingForPlayer) {
issues.push("Missing black player for active game");
}
if (gameData.moveCount < 0 || gameData.moveCount > 1000) {
issues.push(`Invalid move count: ${gameData.moveCount}`);
}
if (issues.length > 0) {
console.warn("Game state issues:", issues);
}
return issues.length === 0;
};Error Recovery Patterns
// Implement retry logic for transient failures
const withRetry = async <T>(
operation: () => Promise<T>,
maxRetries: number = 3,
delay: number = 1000
): Promise<T> => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
if (attempt === maxRetries) {
throw error;
}
console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
delay *= 2; // Exponential backoff
}
}
throw new Error("Max retries exceeded");
};
// Usage
const gameData = await withRetry(() =>
fetchGameAccount(rpc, gameAccountPDA)
);Performance Optimization
Efficient State Updates
// Batch multiple state updates
const updateGameState = async (gameAccountPDA: Address) => {
const [gameAccount, playerStats] = await Promise.all([
fetchGameAccount(rpc, gameAccountPDA),
fetchPlayerStats(player)
]);
// Update UI with both pieces of data
updateGameUI(gameAccount.data);
updatePlayerUI(playerStats);
};Caching Strategies
// Simple game state cache
class GameStateCache {
private cache = new Map<string, { data: GameAccount; timestamp: number }>();
private readonly TTL = 5000; // 5 seconds
async getGameState(gameAccountPDA: Address): Promise<GameAccount> {
const key = gameAccountPDA.toString();
const cached = this.cache.get(key);
if (cached && Date.now() - cached.timestamp < this.TTL) {
return cached.data;
}
const gameAccount = await fetchGameAccount(rpc, gameAccountPDA);
this.cache.set(key, {
data: gameAccount.data,
timestamp: Date.now()
});
return gameAccount.data;
}
}Getting Help
If you're still experiencing issues:
- Check the Console: Look for detailed error messages and stack traces
- Verify Network: Ensure you're using the correct RPC endpoints
- Update Dependencies: Make sure you're using the latest SDK version
- Review Examples: Check the SDK Examples for reference implementations
- Community Support: Join our Discord or GitHub discussions for community help
Common Gotchas
- Always use
BigInt()for game IDs and numeric values - Check game status before performing actions
- Use the correct RPC endpoint based on game state
- Validate moves client-side before sending transactions
- Handle both chess rule violations and blockchain errors
- Remember that some operations require specific player roles (creator vs joiner)