Troubleshooting - RumenDamyanov/go-chess GitHub Wiki
Common issues and solutions when working with the go-chess library.
- Installation Issues
- Runtime Errors
- AI Problems
- LLM AI Issues
- API Server Issues
- Performance Problems
- Configuration Issues
- Development Issues
Problem: go get
fails with module not found error
go: module github.com/rumendamyanov/go-chess not found
Solution:
# Ensure you have Go 1.21 or later
go version
# Initialize your module first
go mod init your-project-name
go get github.com/rumendamyanov/go-chess
# If still failing, try with specific version
go get github.com/rumendamyanov/go-chess@latest
Problem: Import paths not resolving correctly
// Wrong
import "go-chess/engine"
// Correct
import "github.com/rumendamyanov/go-chess/engine"
Solution: Always use the full import path.
Problem: Conflicting dependency versions
go: github.com/gin-gonic/gin v1.9.1 requires github.com/something incompatible
Solution:
# Clean module cache
go clean -modcache
# Update dependencies
go mod tidy
go mod download
# Force specific versions if needed
go mod edit -require github.com/gin-gonic/[email protected]
Problem: Moves are being rejected as invalid
move, err := game.ParseMove("e5") // Error: invalid move
Solutions:
// 1. Check if it's your turn
if game.CurrentPlayer() != engine.White {
log.Println("Not white's turn")
}
// 2. Use full notation when ambiguous
move, err := game.ParseMove("e2e4") // More explicit
// 3. Check legal moves first
legalMoves := game.LegalMoves()
for _, legal := range legalMoves {
if legal.String() == "e2e4" {
// Move is legal
}
}
// 4. Validate move before attempting
if game.IsLegalMove(move) {
game.MakeMove(move)
}
Problem: Game state inconsistencies
// Error: game already over
err := game.MakeMove(move)
Solutions:
// Always check game status
switch game.Status() {
case engine.StatusInProgress:
// Game is ongoing, can make moves
case engine.StatusCheckmate:
log.Printf("%s wins by checkmate", game.Winner())
case engine.StatusStalemate:
log.Println("Game drawn by stalemate")
case engine.StatusDraw:
log.Println("Game drawn")
default:
log.Println("Cannot make moves, game is over")
}
Problem: High memory usage or memory leaks
Solutions:
// 1. Properly dispose of games
func processGame() {
game := engine.NewGame()
defer game.Close() // If available
// Use game...
}
// 2. Limit game history
game.SetMaxHistorySize(100) // Limit move history
// 3. Use object pooling for high-volume scenarios
var gamePool = sync.Pool{
New: func() interface{} {
return engine.NewGame()
},
}
func usePooledGame() {
game := gamePool.Get().(*engine.Game)
defer gamePool.Put(game)
game.Reset() // Reset to initial state
// Use game...
}
Problem: AI hangs or doesn't return moves
ctx := context.Background()
move, err := ai.GetBestMove(ctx, game) // Hangs forever
Solutions:
// 1. Use context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
move, err := ai.GetBestMove(ctx, game)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
log.Println("AI timeout, using random move")
// Fallback to quick move
}
}
// 2. Set AI thinking time limits
aiPlayer := ai.NewMinimaxAI(ai.DifficultyMedium)
aiPlayer.SetMaxThinkTime(5 * time.Second)
// 3. Use simpler AI for testing
randomAI := ai.NewRandomAI() // Always fast
Problem: AI makes obviously bad moves
Solutions:
// 1. Increase difficulty
aiPlayer.SetDifficulty(ai.DifficultyHard)
// 2. Use better AI engine
minima7AI := ai.NewMinimaxAI(ai.DifficultyHard)
// instead of
randomAI := ai.NewRandomAI()
// 3. Allow more thinking time
aiPlayer.SetMaxThinkTime(10 * time.Second)
// 4. Enable AI caching
aiPlayer.EnablePositionCache(true)
Problem: AI engines panic or crash
defer func() {
if r := recover(); r != nil {
log.Printf("AI panic recovered: %v", r)
// Use fallback AI
}
}()
move, err := aiPlayer.GetBestMove(ctx, game)
Problem: Authentication failures with LLM providers
Error: 401 Unauthorized - Invalid API key
Solutions:
# 1. Check API key format
export OPENAI_API_KEY="sk-your-full-key-here" # Must start with sk-
# 2. Verify key is active
curl -H "Authorization: Bearer $OPENAI_API_KEY" \
https://api.openai.com/v1/models
# 3. Check key permissions
# Some keys may be restricted to specific models or usage patterns
Problem: Too many requests error
Error: 429 Too Many Requests - Rate limit exceeded
Solutions:
// 1. Implement rate limiting
import "golang.org/x/time/rate"
limiter := rate.NewLimiter(rate.Every(time.Second), 1) // 1 request per second
func callLLM() error {
if err := limiter.Wait(context.Background()); err != nil {
return err
}
// Make LLM call
return llmAI.GetBestMove(ctx, game)
}
// 2. Implement exponential backoff
func callLLMWithBackoff() error {
backoff := time.Second
maxBackoff := time.Minute
for attempts := 0; attempts < 5; attempts++ {
err := llmAI.GetBestMove(ctx, game)
if err == nil {
return nil
}
if strings.Contains(err.Error(), "rate limit") {
time.Sleep(backoff)
backoff = time.Duration(float64(backoff) * 1.5)
if backoff > maxBackoff {
backoff = maxBackoff
}
continue
}
return err // Non-rate-limit error
}
return fmt.Errorf("max retries exceeded")
}
Problem: Model not found or deprecated
Error: Model 'gpt-3.5-turbo-old' not found
Solutions:
// Update to current model names
config := ai.LLMConfig{
Provider: ai.ProviderOpenAI,
Model: "gpt-4o", // Use latest model
// Model: "gpt-3.5-turbo", // Fallback option
}
// Check provider documentation for current models
// OpenAI: https://platform.openai.com/docs/models
// Anthropic: https://docs.anthropic.com/claude/docs/models-overview
Problem: LLM requests timing out
Solutions:
// 1. Increase timeout
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
// 2. Configure HTTP client with longer timeout
httpClient := &http.Client{
Timeout: 120 * time.Second,
}
config := ai.LLMConfig{
HTTPClient: httpClient,
}
// 3. Implement fallback
move, err := llmAI.GetBestMove(ctx, game)
if err != nil {
log.Printf("LLM timeout: %v", err)
// Fall back to traditional AI
fallbackAI := ai.NewRandomAI()
move, err = fallbackAI.GetBestMove(ctx, game)
}
Problem: Server fails to start
Error: listen tcp :8080: bind: address already in use
Solutions:
# 1. Find what's using the port
lsof -i :8080
# or
netstat -an | grep 8080
# 2. Kill the process
sudo kill -9 <PID>
# 3. Use a different port
export CHESS_PORT=8081
# or modify config
// In code
cfg.Server.Port = 8081
Problem: Frontend can't access API
Error: CORS policy blocked request
Solutions:
// 1. Enable CORS in server setup
r.Use(func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
})
// 2. Use cors middleware
import "github.com/gin-contrib/cors"
config := cors.DefaultConfig()
config.AllowAllOrigins = true
r.Use(cors.New(config))
Problem: Invalid JSON in requests
Error: invalid character '}' looking for beginning of value
Solutions:
// 1. Validate JSON before processing
var req struct {
From string `json:"from"`
To string `json:"to"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": "Invalid JSON: " + err.Error()})
return
}
// 2. Provide better error messages
if req.From == "" || req.To == "" {
c.JSON(400, gin.H{"error": "Missing required fields: from, to"})
return
}
Problem: If using database for game persistence
Error: failed to connect to database
Solutions:
// 1. Connection retry logic
func connectWithRetry() (*sql.DB, error) {
var db *sql.DB
var err error
for attempts := 0; attempts < 5; attempts++ {
db, err = sql.Open("postgres", connectionString)
if err == nil {
if err = db.Ping(); err == nil {
return db, nil
}
}
log.Printf("Database connection attempt %d failed: %v", attempts+1, err)
time.Sleep(time.Duration(attempts+1) * time.Second)
}
return nil, fmt.Errorf("failed to connect after 5 attempts: %w", err)
}
// 2. Connection pooling
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(5 * time.Minute)
Problem: AI takes too long to generate moves
Solutions:
// 1. Reduce AI depth
aiPlayer := ai.NewMinimaxAI(ai.DifficultyMedium) // Instead of Expert
// 2. Set time limits
aiPlayer.SetMaxThinkTime(3 * time.Second)
// 3. Use faster algorithms for real-time play
quickAI := ai.NewRandomAI() // For very fast responses
// 4. Enable caching
aiPlayer.EnablePositionCache(true)
aiPlayer.SetCacheSize(10000)
Problem: High memory consumption
Solutions:
// 1. Limit game history
game.SetMaxMoveHistory(100)
// 2. Use object pooling
var boardPool = sync.Pool{
New: func() interface{} {
return engine.NewBoard()
},
}
// 3. Regular garbage collection
import "runtime"
go func() {
for {
time.Sleep(30 * time.Second)
runtime.GC()
}
}()
// 4. Profile memory usage
import _ "net/http/pprof"
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// Then visit http://localhost:6060/debug/pprof/
Problem: High CPU utilization
Solutions:
// 1. Limit concurrent AI calculations
semaphore := make(chan struct{}, 2) // Max 2 concurrent AI operations
func getAIMove() error {
semaphore <- struct{}{} // Acquire
defer func() { <-semaphore }() // Release
return aiPlayer.GetBestMove(ctx, game)
}
// 2. Use worker pools
type aiJob struct {
game *engine.Game
result chan aiResult
}
type aiResult struct {
move engine.Move
err error
}
func aiWorker(jobs <-chan aiJob) {
for job := range jobs {
move, err := aiPlayer.GetBestMove(context.Background(), job.game)
job.result <- aiResult{move, err}
}
}
Problem: Configuration file errors
Error: failed to parse config.json
Solutions:
# 1. Validate JSON syntax
cat config.json | jq . # Validates JSON
# 2. Check file permissions
ls -la config.json
chmod 644 config.json
# 3. Use configuration validation
func validateConfig(cfg *config.Config) error {
if cfg.Server.Port < 1024 || cfg.Server.Port > 65535 {
return fmt.Errorf("invalid port: %d", cfg.Server.Port)
}
if cfg.LLMAI.Enabled {
for name, provider := range cfg.LLMAI.Providers {
if provider.APIKey == "" {
return fmt.Errorf("missing API key for provider: %s", name)
}
}
}
return nil
}
Problem: Environment variables not being read
export CHESS_PORT=8080 # Not taking effect
Solutions:
# 1. Check if variable is set
echo $CHESS_PORT
# 2. Use env command to verify
env | grep CHESS
# 3. Check variable in Go
port := os.Getenv("CHESS_PORT")
if port == "" {
log.Println("CHESS_PORT not set, using default")
}
Problem: Compilation failures
./main.go:10:2: no required module provides package github.com/rumendamyanov/go-chess/engine
Solutions:
# 1. Ensure module is properly initialized
go mod init your-project
go mod tidy
# 2. Check Go version
go version # Should be 1.21+
# 3. Clear module cache if corrupted
go clean -modcache
Problem: Tests failing unexpectedly
go test ./...
FAIL: TestMakeMove (0.00s)
Solutions:
# 1. Run tests with verbose output
go test -v ./...
# 2. Run specific test
go test -v -run TestMakeMove
# 3. Check for race conditions
go test -race ./...
# 4. Clean test cache
go clean -testcache
Problem: Circular import dependencies
package command-line-arguments
imports A
imports B
imports A: import cycle not allowed
Solutions:
// 1. Move shared code to separate package
// Create shared/common package for shared types
// 2. Use interfaces to break cycles
type GameInterface interface {
MakeMove(move Move) error
LegalMoves() []Move
}
// 3. Restructure packages to avoid cycles
When reporting issues, include:
func collectDebugInfo() map[string]interface{} {
return map[string]interface{}{
"go_version": runtime.Version(),
"os": runtime.GOOS,
"arch": runtime.GOARCH,
"game_fen": game.FEN(),
"last_moves": game.MoveHistory(),
"ai_config": aiPlayer.GetConfig(),
"error_trace": string(debug.Stack()),
}
}
- GitHub Issues: github.com/rumendamyanov/go-chess/issues
- Discussions: github.com/rumendamyanov/go-chess/discussions
- Wiki: Check other wiki pages for detailed guides
-
Examples: Look at
examples/
directory for working code
For production issues or custom development:
- Create detailed issue reports with reproduction steps
- Include debug information and logs
- Consider contributing fixes back to the project
Remember: Most issues are configuration or usage related. Double-check the documentation and examples before diving deep into debugging!