NousResearch/hermes-agent

Retry logic for transient startup and TLS failures

Cluster 1526 · gateway. Data as of

Nothing open on GitHub. Fix merged in #1535 on 16 Mar 2026. Check it covers your case.

The short answer

0open issues
0open PRs
3items grouped
Known?
Yes. 3 related items grouped here, 1 issue already closed.
Fix event
A fix was merged to main in PR #1535. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this resolved, deciding PR #1535. That is separate from the fix event.
Still open
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.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #1535 fix(telegram): retry on transient TLS failures during connect and send

    merged to main

    files: gateway/platforms/telegram.py

    Show the diff
    • gateway/platforms/telegram.py
      @@ -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()
       
      @@ -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(

1 PR closed without merging

From the close comments: other 1

  • #1527 fix(telegram): retry on transient TLS failures during connect and send · other → #1535
Closed issues · 1
  • Closed#1526[Bug]: Telegram connection fails intermittently on startup due to transient TLS reset with no retry
How this cluster was grouped
  • Link evidence (structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
  • Pairwise link density 0.67: the share of item 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.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Subsystem
gateway
Merged PRs
#1535
Closed issues
1
Attempts
1 PR closed without merging
Fix link checked
16 Mar 2026. This confirmation is old and may be out of date (a revert or reopen would not have invalidated it here); re-verify.

For your agent

The same facts, typed.