# NousResearch/hermes-agent · Retry logic for transient startup and TLS failures

## Open now

- Nothing open on GitHub.

These members address missing or insufficient retry behavior when transient TLS errors or other retryable failures occur during gateway startup and Telegram connection/send operations. Both the gateway restart and Telegram-specific TLS retry cases are represented.

## TL;DR

- 3 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 1 PR(s) closed without merging.
- A fix was merged to main in PR #1535. Verify it addresses your case.
- **Recommended:** read PR #1535 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `resolved` · deciding PR(s): `pr-1535`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.67`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #1526: \[Bug\]: Telegram connection fails intermittently on startup due to transient TLS reset with no retry

## Prior & competing PRs

- **#1535: fix(telegram): retry on transient TLS failures during connect and send**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `gateway/platforms/telegram.py`
  - fix hunks:
  - `gateway/platforms/telegram.py`

    ```diff
    @@ -202,8 +202,26 @@ async def connect(self) -> bool:
                     self._handle_media_message
                 ))
                 
    -            # Start polling in background
    -            await self._app.initialize()
    +            # Start polling — retry initialize() for transient TLS resets
    +            try:
    +                from telegram.error import NetworkError, TimedOut
    +            except ImportError:
    +                NetworkError = TimedOut = OSError  # type: ignore[misc,assignment]
    +            _max_connect = 3
    +            for _attempt in range(_max_connect):
    +                try:
    +                    await self._app.initialize()
    +                    break
    +                except (NetworkError, TimedOut, OSError) as init_err:
    +                    if _attempt < _max_connect - 1:
    +                        wait = 2 ** _attempt
    +                        logger.warning(
    +                            "[%s] Connect attempt %d/%d failed: %s — retrying in %ds",
    +                            self.name, _attempt + 1, _max_connect, init_err, wait,
    +                        )
    +                        await asyncio.sleep(wait)
    +                    else:
    +                        raise
                 await self._app.start()
                 loop = asyncio.get_running_loop()
     
    ```

    ```diff
    @@ -334,32 +352,47 @@ async def send(
                 message_ids = []
                 thread_id = metadata.get("thread_id") if metadata else None
                 
    +            try:
    +                from telegram.error import NetworkError as _NetErr
    +            except ImportError:
    +                _NetErr = OSError  # type: ignore[misc,assignment]
    +
                 for i, chunk in enumerate(chunks):
    -                # Try Markdown first, fall back to plain text if it fails
    -                try:
    -                    msg = await self._bot.send_message(
    -                        chat_id=int(chat_id),
    -                        text=chunk,
    -                        parse_mode=ParseMode.MARKDOWN_V2,
    -                        reply_to_message_id=int(reply_to) if reply_to and i == 0 else None,
    -                        message_thread_id=int(thread_id) if thread_id else None,
    -                    )
    -                except Exception as md_error:
    -                    # Markdown parsing failed, try plain text
    -                    if "parse" in str(md_error).lower() or "markdown" in str(md_error).lower():
    -                        logger.warning("[%s] MarkdownV2 parse failed, falling back to plain text: %s", self.name, md_error)
    -                        # Strip MDV2 escape backslashes so the user doesn't
    -                        # see raw backslashes littered through the message.
    -                        plain_chunk = _strip_mdv2(chunk)
    -                        msg = await self._bot.send_message(
    -                            chat_id=int(chat_id),
    -                            text=plain_chunk,
    -                            parse_mode=None,  # Plain text
    -                            reply_to_message_id=int(reply_to) if reply_to and i == 0 else None,
    -                            message_thread_id=int(thread_id) if thread_id else None,
    -                        )
    -                    else:
    -                        raise  # Re-raise if not a parse error
    +                msg = None
    +                for _send_attempt in range(3):
    +                    try:
    +                        # Try Markdown first, fall back to plain text if it fails
    +                        try:
    +                            msg = await self._bot.send_message(
    +                                chat_id=int(chat_id),
    +                                text=chunk,
    +                                parse_mode=ParseMode.MARKDOWN_V2,
    +                                reply_to_message_id=int(reply_to) if reply_to and i == 0 else None,
    +                                message_thread_id=int(thread_id) if thread_id else None,
    +                            )
    +                        except Exception as md_error:
    +                            # Markdown parsing failed, try plain text
    +                            if "parse" in str(md_error).lower() or "markdown" in str(md_error).lower():
    +                                logger.warning("[%s] MarkdownV2 parse failed, falling back to plain text: %s", self.name, md_error)
    +                                plain_chunk = _strip_mdv2(chunk)
    +                                msg = await self._bot.send_message(
    +                                    chat_id=int(chat_id),
    +                                    text=plain_chunk,
    +                                    parse_mode=None,
    +                                    reply_to_message_id=int(reply_to) if reply_to and i == 0 else None,
    +                                    message_thread_id=int(thread_id) if thread_id else None,
    +                                )
    +                            else:
    +                                raise
    +                        break  # success
    +                    except _NetErr as send_err:
    +                        if _send_attempt < 2:
    +                            wait = 2 ** _send_attempt
    +                            logger.warning("[%s] Network error on send (attempt %d/3), retrying in %ds: %s",
    +                                           self.name, _send_attempt + 1, wait, send_err)
    +                            await asyncio.sleep(wait)
    +                        else:
    +                            raise
                     message_ids.append(str(msg.message_id))
                 
                 return SendResult(
    ```
- **#1527: fix(telegram): retry on transient TLS failures during connect and send**. closed without merge
  - maintainer direction: category `uncategorized`; implication `needs_llm`; refs `#1535`

## Fix status

- Merged to main in PR #1535 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-03-16`. This confirmation is old and may be out of date (a revert/reopen would not have invalidated it here); re-verify.
- Subsystem: `gateway`
