diff --git a/gateway/platforms/telegram.py b/gateway/platforms/telegram.py index 884ef9c45..265329602 100644 --- a/gateway/platforms/telegram.py +++ b/gateway/platforms/telegram.py @@ -299,9 +299,11 @@ class TelegramAdapter(BasePlatformAdapter): # Exhausted retries — fatal message = ( - "Another Telegram bot poller is already using this token. " + "Another process is already polling this Telegram bot token " + "(possibly OpenClaw or another Hermes instance). " "Hermes stopped Telegram polling after %d retries. " - "Make sure only one gateway instance is running for this bot token." + "Only one poller can run per token — stop the other process " + "and restart with 'hermes start'." % MAX_CONFLICT_RETRIES ) logger.error("[%s] %s Original error: %s", self.name, message, error) diff --git a/hermes_cli/claw.py b/hermes_cli/claw.py index 3ab6bf9a8..d0bfd73d2 100644 --- a/hermes_cli/claw.py +++ b/hermes_cli/claw.py @@ -52,6 +52,41 @@ _OPENCLAW_SCRIPT_INSTALLED = ( # Known OpenClaw directory names (current + legacy) _OPENCLAW_DIR_NAMES = (".openclaw", ".clawdbot", ".moldbot") +def _warn_if_gateway_running(auto_yes: bool) -> None: + """Check if a Hermes gateway is running with connected platforms. + + Migrating bot tokens while the gateway is polling will cause conflicts + (e.g. Telegram 409 "terminated by other getUpdates request"). Warn the + user and let them decide whether to continue. + """ + from gateway.status import get_running_pid, read_runtime_status + + if not get_running_pid(): + return + + data = read_runtime_status() or {} + platforms = data.get("platforms") or {} + connected = [name for name, info in platforms.items() + if isinstance(info, dict) and info.get("state") == "connected"] + if not connected: + return + + print() + print_error( + "Hermes gateway is running with active connections: " + + ", ".join(connected) + ) + print_info( + "Migrating bot tokens while the gateway is active will cause " + "conflicts (Telegram, Discord, and Slack only allow one active " + "session per token)." + ) + print_info("Recommendation: stop the gateway first with 'hermes stop'.") + print() + if not auto_yes and not prompt_yes_no("Continue anyway?", default=False): + print_info("Migration cancelled. Stop the gateway and try again.") + sys.exit(0) + # State files commonly found in OpenClaw workspace directories that cause # confusion after migration (the agent discovers them and writes to them) _WORKSPACE_STATE_GLOBS = ( @@ -252,6 +287,10 @@ def _cmd_migrate(args): print_info(f"Workspace: {workspace_target}") print() + # Check if a gateway is running with connected platforms — migrating tokens + # while the gateway is active will cause conflicts (e.g. Telegram 409). + _warn_if_gateway_running(auto_yes) + # Ensure config.yaml exists before migration tries to read it config_path = get_config_path() if not config_path.exists():