Skip to content

fix(telegram+tools): prevent connect hang with retry watchdog and fresh app per attempt (#67498, #68915) - #70884

Merged
kshitijk4poor merged 3 commits into
NousResearch:mainfrom
kshitijk4poor:salvage/70546-telegram-connect-hang-and-spawn-rewrite
Jul 24, 2026
Merged

kshitijk4poor merged 3 commits into
NousResearch:mainfrom
kshitijk4poor:salvage/70546-telegram-connect-hang-and-spawn-rewrite

Conversation

@kshitijk4poor

Copy link
Copy Markdown
Contributor

Summary

Fixes two bugs: Telegram gateway connect hang (#67498) and spawn_local compound-background deadlock (#68915).

Changes

  • plugins/platforms/telegram/adapter.py: total watchdog deadline for the connect retry loop, fresh Application rebuild per failed attempt, BaseException logging for CancelledError
  • tools/process_registry.py: apply _rewrite_compound_background() in spawn_local() to prevent A && B & subshell deadlocks
  • tests/tools/test_process_registry.py: 4 new tests for compound-background rewrite in spawn_local
  • contributors/emails/agent@hermes.dev: contributor email mapping (commits authored by agent identity, PR by @webtecnica)

Salvage of #70546 by @webtecnica — cherry-picked with authorship preserved.

Notes

  • The PR bundles two unrelated fixes. The contributor also opened fix(tools): prevent worker deadlock when agent backgrounds server via shell & (#68915) #70549 for the spawn_local fix separately. We salvage both here for expediency.
  • Handler registration is duplicated between initial setup and the rebuild-in-finally block — noted as a refactor follow-up (extract to _register_handlers() method), not blocking.
  • 3-agent parallel review found no regressions. The _shutdown_abandoned_app call in the finally block is already best-effort (catches all exceptions internally). The spawn_via_env path correctly passes rewrite_compound_background=False since it wraps commands in its own nohup structure.

Validation

Before After
Telegram connect hang on CancelledError silent task exit logged + propagated
Stale app on retry partially-initialized state fresh rebuild per attempt
spawn_local compound background subshell deadlock rewritten to A && { B & }
Tests (test_process_registry.py) 113 117/117 pass
Lint (ruff) clean

Hermes Agent and others added 3 commits July 24, 2026 13:17
…ker deadlock on server backgrounding

Issue NousResearch#68915: when the agent runs a compound command with trailing & (e.g.
`cd /app && node server.js &`), bash parses it as `(A && B) &` — a subshell
that holds the stdout pipe open forever when B is a long-running server.
The existing _rewrite_compound_background in terminal_tool.py correctly
rewrites this to `A && { B & }` to avoid the subshell fork, but it was only
applied in the foreground execute() path (tools/environments/base.py).

The background spawn_local() path bypasses base.py entirely and passed the
raw command directly to Popen/PTY, leaving the deadlock unmitigated.

Fix: apply _rewrite_compound_background in spawn_local() before the command
is passed to Popen or PTY spawn. Uses a lazy import to avoid circular
dependency (terminal_tool imports process_registry).

- PTY spawn path: now uses safe_command (rewritten)
- Popen spawn path: now uses safe_command (rewritten)
- Session.command still stores the original (unrewritten) command for display
- Simple `cmd &` is left unchanged (no subshell bug)

Tests: 4 regression tests verifying (1) compound is rewritten, (2) simple bg
is preserved, (3) multi-line compounds are rewritten, (4) session.command
stores original.
… per attempt (NousResearch#67498)

The Telegram adapter's connect retry loop could silently stall after
'Connecting to Telegram (attempt 1/8)...' with the event loop permanently
parked in select() — all threads idle, no attempt 2/8 ever scheduled.

Root cause analysis:
- The retry loop reused the same  Application object across all
  8 attempts. After a failed initialize() the app could be in a partially-
  initialized state (closed httpx transports from ,
  or  flag set before the hang) causing subsequent calls
  to silently skip real initialization.
- CancelledError (a BaseException, not an Exception) propagated silently
  through all except handlers with no logging — the task driving the retry
  loop could exit without any trace.
- No total watchdog bound existed for the entire retry loop; only per-attempt
  timeouts via _await_with_thread_deadline. If the loop itself stalled
  between attempts (between-attempt sleep, cleanup, or scheduling), there
  was no timeout to catch it.

Fixes:
1. **Total watchdog deadline**: Compute a total deadline for the entire
   connect loop (8 attempts × init_timeout + 120s margin). Before each
   attempt, check the wall clock; if exceeded, raise OSError immediately
   instead of attempting another initialize().
2. **Fresh Application per retry**: On each failed attempt, rebuild
    via  and re-register all handlers. The old
   app is best-effort shutdown with . This ensures
   each retry starts with a clean slate — no stale transports, no stale
    flag, no leaked state from the previous attempt.
3. **BaseException logging + propagation**: Added
   (placed LAST after all other handlers) to log CancelledError and other
   non-Exception signals before propagating. Previously these exited the
   retry loop silently with no log message.
4. ** block for app rebuild**: The  clause runs after
   every failed attempt that isn't the last, rebuilding the app and
   discarding the old one regardless of which exception class caused the
   failure.
@kshitijk4poor
kshitijk4poor enabled auto-merge (rebase) July 24, 2026 17:31
@kshitijk4poor
kshitijk4poor merged commit f7e2c0e into NousResearch:main Jul 24, 2026
34 checks passed
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/plugins Plugin system and bundled plugins comp/tools Tool registry, model_tools, toolsets platform/telegram Telegram bot adapter tool/terminal Terminal execution and process management sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Jul 24, 2026
@kshitijk4poor
kshitijk4poor deleted the salvage/70546-telegram-connect-hang-and-spawn-rewrite branch August 5, 2026 07:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins comp/tools Tool registry, model_tools, toolsets P2 Medium — degraded but workaround exists platform/telegram Telegram bot adapter sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages tool/terminal Terminal execution and process management type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants