diff --git a/src/main.py b/src/main.py index 0a136f5..1d3ef40 100644 --- a/src/main.py +++ b/src/main.py @@ -342,14 +342,33 @@ def main(): if rlist: try: conn, _ = _hermes_socket.accept() + # select() only says a connection is PENDING — the + # client's bytes may not have arrived yet. The accepted + # socket inherits the listener's non-blocking mode, so + # recv() raised BlockingIOError (WinError 10035), the + # error was swallowed, and the caller saw a timeout. + # That is the root of every "no response from bot + # (socket timeout)" and of the retry loops built around + # it — one of which paused the bot for 4h50m. + conn.setblocking(True) + conn.settimeout(2.0) data = conn.recv(1024).decode().strip().lower() _reply = handle_hermes_command(data, controllers) if _reply is not None: conn.sendall(_reply.encode()) conn.close() - except Exception: - pass - except Exception: + except Exception as e: + # Never swallow this silently. A raising handler leaves + # the connection in CLOSE_WAIT and every control command + # times out, which reads exactly like "the bot is wedged" + # and cost a long debugging detour on 2026-08-28. + Logger.error(f"hermes command {data!r} failed: {type(e).__name__}: {e}") + try: + conn.close() + except Exception: + pass + except Exception as e: + Logger.debug(f"hermes poll loop error: {type(e).__name__}: {e}") time.sleep(0.5) if _hermes_socket is not None: