Files
my-botty/docs/recovery_and_error_logging.md
alex f705091991 Send error message + screenshot to Discord on run failures
Adds Messenger.send_error across the Discord (red embed with screenshot attached)
and generic (text-only) APIs. Bot._save_error_screenshot now also pushes the
failure to the configured messenger after saving the screenshot to disk, so each
approach/battle/exception failure is reviewable in Discord with the visual.

Gated by new config discord_log_errors ([general], default 1) and the
[discord_events] error toggle. Both optional/backward-compatible. Verified wiring
and the suppression path via a stubbed messenger.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 15:55:41 +02:00

4.9 KiB

Recovery & Error Logging

This document describes the failure-recovery and diagnostics features that let the bot survive broken runs and makes every failure reviewable after the fact.

1. Comprehensive failure logging

Every town action (src/town/a1.pya5.py) and every run approach() method (src/run/*.py) now logs the specific step that failed via Logger.error instead of silently returning False. Examples of what now gets logged:

  • A1 resurrect: open_npc_menu(KASHYA) failed — NPC not found or menu did not open
  • A4 open_wp: waypoint template A4_WP/A4_WP_2 not found — WP may be obscured or template mismatch
  • Vizier approach: use_wp('River of Flame') failed
  • Mephisto approach: go_to_act(3) failed
  • gamble: go_to_act(4) failed from A1_TOWN_START — cannot reach Jamella

Town-manager fallbacks (resurrect, identify, open_stash, gamble, stash, heal) also log when their go_to_act recovery hop fails, so you can tell whether the problem was reaching the NPC, opening the menu, pressing the button, or the panel never opening.

This means that "if Cain is missing, if the WP is not enabled, if some other game factor is not in place" the reason ends up in the log.

2. Error screenshots

When a run fails, the bot saves a screenshot so logs and visuals can be compared.

  • Where: log/screenshots/error/
  • Filename: error_<run_name>_<reason>_g<game#>_r<run#>_<YYYYMMDD_HHMMSS>.png e.g. error_run_vizier_approach_failed_g12_r47_20260607_143501.png
  • When: captured on three failure types inside Bot._run_wrapper:
    • approach_failed — couldn't reach the boss
    • battle_failed — boss fight didn't complete
    • exception_<ExceptionType> — an uncaught exception during battle
  • The g<game#>_r<run#> in the filename matches the game/run fields in the structured event log (log/stats/events_*.jsonl) and the Logger output, so a screenshot can be lined up with its log lines.

Discord delivery

When a run fails the bot also sends the error message and the screenshot to Discord (as a red embed with the screenshot attached). For the generic webhook API a text-only message is sent (the screenshot still lands on disk). This is on by default and can be turned off two ways:

  • [general] discord_log_errors=0 — master switch for error → Discord
  • [discord_events] error=0 — fine-grained event toggle (Discord API only)

Config

config/params.ini[general]:

; save a screenshot every time a run fails (falls back to info_screenshots if unset)
error_screenshots=1
; also send the error message + screenshot to Discord on each run failure
discord_log_errors=1

config/params.ini[discord_events]:

; send run-failure notifications (message + error screenshot)
error=1

Set error_screenshots=0 to turn off error screenshots entirely. Old screenshots are size-rotated using the info_* limits in [log_rotation] (or optional error_max_files / error_max_mb if you add them).

3. Auto-disable broken runs (recovery)

Previously, repeated failures of one run could stall the whole bot. Now each run has its own consecutive failure counter:

  • Every failed attempt of a run increments its counter; a single success resets it.
  • When a run reaches the threshold of consecutive failures, it is disabled for the rest of the session. The bot keeps doing the other runs instead of stopping.
  • Disabling is logged and sent to Discord (if enabled): DISABLING run_vizier for this session: it failed 5 consecutive times. …
  • The disable is session-only / in-memory — it does not edit params.ini. Restarting the bot re-enables the run. This is intentional so a transient problem (e.g. a game hiccup) doesn't permanently change your config.
  • If all runs end up disabled there are no routes left, so the bot saves a session report and stops cleanly (kills the game and exits) rather than looping over empty games or pointlessly restarting D2R.

Config

config/params.ini[general]:

; after this many CONSECUTIVE failures of the same run, disable just that run
; for the rest of the session and keep doing the others. A success resets the count.
disable_run_after_failures=5

Set this high (e.g. 9999) to effectively disable the auto-disable behaviour.

Implementation notes

  • src/bot.py
    • __init__: _run_failure_counts, _disabled_runs, _max_run_failures
    • _record_run_result(run_name, failed): counts failures, disables runs, resets on success
    • _save_error_screenshot(run_name, reason): writes the diagnostic screenshot
    • _run_wrapper(...): calls both on each failure path (approach / battle / exception)
  • src/config.py: parses error_screenshots and disable_run_after_failures (both via _select_optional, so existing configs keep working).
  • src/utils/log_rotation.py: routes the error/ directory through rotation.