Skip to content

fix(runtime): replay failed reconnect result to queued callers - #310

Merged
chigwell merged 1 commit into
chigwell:mainfrom
axisrow:fix/298-reuse-failed-reconnect
Oct 17, 2026
Merged

chigwell merged 1 commit into
chigwell:mainfrom
axisrow:fix/298-reuse-failed-reconnect

Conversation

@axisrow

@axisrow axisrow commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #292 (issue #298): reconnects are serialized per client, but only a successful reconnect result was reused. When _force_reconnect failed, every queued caller ran its own full disconnect+connect and hit the 55 s tool timeout with an opaque GEN-TIMEOUT instead of the real reconnect error.

Cache the reconnect outcome per client under the same lock key and replay it to waiters while it is fresh (_RECONNECT_FAILURE_TTL = 30 s); a successful reconnect clears the cache so the next verification cycle runs normally.

Before

The connection drops while 3 tool calls share one client (streamable HTTP). Only the first caller actually reconnects; each queued caller ran its own full disconnect+connect (up to 30 s each, serially):

SCENARIO: 3 concurrent tool calls hit one client while the network is down
connect() attempts made: 3          # one full disconnect+connect per caller
wall time for the batch: ~90s       # far past the 55s tool timeout
-> the 2nd/3rd callers die with: "Error: ... (code: GEN-TIMEOUT)"

The last callers got an opaque timeout instead of the actual reconnect error.

After

The first failed reconnect is cached under the connection lock and replayed to every queued caller while it is fresh:

SCENARIO: 3 concurrent tool calls hit one client while the network is down
connect() attempts made: 1
-> all 3 callers get the real error, e.g.
   "Error: Reconnecting to Telegram timed out after 30s."

If the network recovers, a successful reconnect clears the cache and the normal verify-ping cycle resumes; a stale failure (older than 30 s) triggers a fresh reconnect attempt.

Changes

  • telegram_mcp/runtime.py: _reconnect_once wraps both _force_reconnect call sites in ensure_connected and records (timestamp, error) on failure / clears on success; _raise_cached_reconnect_failure replays a fresh failure on the not is_connected() path instead of repeating the attempt.
  • tests/test_runtime.py: autouse fixture resets the reconnect state dicts (they are keyed by id(client), which CPython reuses across tests); new tests: 3 concurrent callers with a failing connect() produce exactly one connect/disconnect attempt and all get the original ConnectionError; a stale cached failure is retried and cleared on success.

Tests

uv run pytest -q — 1194 passed (full suite, includes the 2 new tests).

Fixes #298

🤖 Generated with Claude Code

chigwell#292 serialized reconnects but only reused a successful one: when
_force_reconnect failed, every queued caller ran its own full
disconnect+connect (up to 30s each) and hit the 55s tool timeout with an
opaque GEN-TIMEOUT instead of the real reconnect error.

Cache the outcome of the last reconnect per client and replay it to
waiters while it is fresh (_RECONNECT_FAILURE_TTL); a successful
reconnect clears the cache so the next verification cycle runs normally.

Fixes chigwell#298

Co-Authored-By: Claude Code <[email protected]>
@chigwell
chigwell merged commit a829702 into chigwell:main Oct 17, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

follow-up to #292: a failed reconnect is not reused; concurrent callers still reconnect serially

2 participants