Skip to content

Gateway interrupt loop: get_pending_message never clears, causing infinite recursion #21

@alt-glitch

Description

@alt-glitch

Bug

When a user sends a new message while the agent is processing a long-running tool call (e.g., a terminal command), the gateway enters an infinite recursion loop that spams the user with repeated (No response generated)\n---\n[Interrupted - processing your new message] messages on Telegram.

Reproduction

  1. Send a message to the Telegram bot that triggers a long-running tool call (e.g., a command that takes 10+ seconds)
  2. While the agent is processing, send a second message like "whats happening"
  3. The bot starts spamming (No response generated) --- [Interrupted - processing your new message] indefinitely

Root Cause

Two bugs in the interrupt handling create an infinite loop:

Bug 1: get_pending_message uses .get() instead of .pop() (gateway/platforms/base.py:289)

def get_pending_message(self, session_key: str) -> Optional[MessageEvent]:
    """Get and clear any pending message for a session."""
    return self._pending_messages.get(session_key)  # ← never actually clears!

The docstring says "Get and clear" but it only gets — the pending message stays in the dict forever.

Bug 2: _active_sessions interrupt Event is never reset (gateway/platforms/base.py:213)

When a new message arrives during processing, _active_sessions[chat_id].set() is called but the Event is never cleared between recursive _run_agent calls. Since has_pending_interrupt() checks _active_sessions[session_key].is_set(), it returns True on every subsequent check.

The Loop

1. User sends "whats happening" during processing
2. Adapter stores in _pending_messages[chat_id], sets _active_sessions[chat_id].set()
3. monitor_for_interrupt detects it → calls agent.interrupt()
4. Agent breaks out → final_response = None → "(No response generated)"
5. run.py:567 calls adapter.get_pending_message(chat_id) → returns event
   ⚠ Message stays in _pending_messages (.get not .pop)
6. Sends "(No response generated)\n---\n[Interrupted]" to Telegram
7. Recursively calls _run_agent("whats happening")
8. New monitor_for_interrupt starts → has_pending_interrupt still True!
9. Immediately interrupts new agent before it makes any API call
10. GOTO step 4 — infinite loop, each iteration sends a Telegram message

Proposed Fix

gateway/platforms/base.py:289 — use .pop():

def get_pending_message(self, session_key: str) -> Optional[MessageEvent]:
    """Get and clear any pending message for a session."""
    return self._pending_messages.pop(session_key, None)

gateway/run.py — clear the adapter's interrupt state before recursing into _run_agent:

# After getting the pending message, clear interrupt state
if pending:
    # Clear the interrupt event so the next _run_agent call doesn't immediately re-trigger
    if hasattr(adapter, '_active_sessions') and source.chat_id in adapter._active_sessions:
        adapter._active_sessions[source.chat_id].clear()

Affected Files

  • gateway/platforms/base.pyget_pending_message(), handle_message()
  • gateway/run.py_run_agent() interrupt handling and recursive call (lines 560-592)

Impact

Every interrupt during a long tool call triggers an infinite message spam to the user's Telegram chat. The only way to stop it is to kill the gateway process.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions