HKUDS/nanobot · error · RuntimeError

Telegram application is restarting; message not delivered

Error message

Telegram application is restarting; message not delivered

What it means

In _wait_for_app(), a send (or send_delta) was attempted while the Telegram application is not live: _app_ready stayed unset after waiting APP_RESTART_SEND_WAIT_SECONDS for start() to finish rebuilding the app. Returning quietly would let the channel manager count the message as delivered, so this RuntimeError explicitly signals non-delivery so the buffered send can be retried later.

Solutions

  1. Let the channel manager retry the buffered message once the app is rebuilt
  2. Check gateway logs for why start() is repeatedly rebuilding (polling stalls, network errors)
  3. Increase APP_RESTART_SEND_WAIT_SECONDS only if rebuilds are legitimately slow
Defensive patterns

Strategy: retry

When it happens

Trigger: Thrown at nanobot/channels/telegram/runtime.py:747 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of HKUDS/nanobot@42f37dc4c0 (2026-08-26). Data as JSON: /api/errors/3d00d0f6e5e491dc. Report an issue: GitHub.

Appendix: source

Thrown at nanobot/channels/telegram/runtime.py:747

        bound webhook port raises OSError.
        """
        return isinstance(exc, NetworkError | TimedOut | asyncio.TimeoutError)

    async def _wait_for_app(self) -> TelegramApplication | None:
        """Return the live app, briefly waiting out an in-flight rebuild.

        Returning quietly while ``start()`` rebuilds would let the manager count
        the message as delivered, so raise once the wait runs out. None means the
        channel is stopped: nothing left to deliver.
        """
        if self._app_ready.is_set() and self._app is not None:
            return self._app
        if not self._running:
            return None
        with suppress(asyncio.TimeoutError):
            await asyncio.wait_for(self._app_ready.wait(), APP_RESTART_SEND_WAIT_SECONDS)
        if not self._app_ready.is_set() or self._app is None:
            raise RuntimeError("Telegram application is restarting; message not delivered")
        return self._app

    def _note_poll_ok(self) -> None:
        # HTTP error statuses count too: the watchdog detects transport stalls,
        # not logical failures.
        self._last_poll_ok = time.monotonic()

    async def _watch_polling(self) -> bool:
        """Idle until stop(); in polling mode, return True when getUpdates goes stale."""
        watch = self.config.mode != "webhook"
        while self._running:
            await asyncio.sleep(POLL_WATCH_INTERVAL)
            if watch and time.monotonic() - self._last_poll_ok > POLL_STALE_SECONDS:
                return True
        return False

    async def _idle(self, seconds: float) -> None:
        """Sleep in short steps so stop() stays responsive."""

View on GitHub (pinned to 42f37dc4c0)