Skip to content

Bot abandons game mid-play when stream drops (StopIteration) #1184

@metaember

Description

@metaember

Bug Description

When the game stream encounters a StopIteration (stream ends due to network blip, Lichess closing the connection, etc.), the bot unconditionally exits the game loop — even if the game is still in progress. This causes the bot to flag on time.

Root Cause

In lichess_bot.py lines 780-783:

except (HTTPError, ReadTimeout, RemoteDisconnected, ChunkedEncodingError, RequestsConnectionError,
        StopIteration) as e:
    stopped = isinstance(e, StopIteration)
    stay_in_game = not stopped and (move_attempted or game_is_active(li, game.id))

Issue 1: When StopIteration fires, stay_in_game is set to False unconditionally (not stopped is False), bypassing both the move_attempted check and the game_is_active() check. A stream ending doesn't mean the game ended — it could be a transient network issue.

Issue 2: move_attempted is reset to False at the top of each loop iteration (line 726). So if the bot made a move on iteration N, and the exception fires on iteration N+1, move_attempted is already False. The fallback game_is_active() queries /api/account/playing, which can return stale data if called immediately after making a move (race condition).

Steps to Reproduce

  1. Bot is playing a game
  2. Bot makes a move successfully (HTTP 200 from /api/bot/game/{id}/move/{move})
  3. Game stream connection drops (StopIteration on next(lines))
  4. Bot exits game loop, kills engine
  5. Opponent makes a move, bot never responds
  6. Bot flags on time

Observed Behavior

From logs:

20:26:56 - Bot plays Qe2, move accepted (HTTP 200)
20:27:11 - Bot checks /api/account/playing, concludes game is over, quits engine
20:27:11 - "Game over" logged, engine process exits
20:32:08 - gameFinish event arrives with status "outoftime" — bot flagged

The bot quit the engine ~15 seconds after its last move, while the game was still ongoing.

Suggested Fix

On StopIteration, the bot should verify the game is still active via game_is_active() (with a small delay to avoid the race condition) before exiting, rather than unconditionally giving up:

except (HTTPError, ReadTimeout, RemoteDisconnected, ChunkedEncodingError, RequestsConnectionError,
        StopIteration) as e:
    stopped = isinstance(e, StopIteration)
    if stopped:
        time.sleep(1)
    stay_in_game = move_attempted or game_is_active(li, game.id)

Environment

  • lichess-bot version: 2026.2.13.1 (commit 960bcad)
  • Running in Docker on Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions