HKUDS/Vibe-Trading · error · RuntimeError

WhatsApp channel is not connected

Error message

WhatsApp channel is not connected

What it means

Raised by WhatsApp channel send() when self._client is None or the channel's _connected flag is false. It means send() was called before a successful connect() (or after a disconnect event cleared the client). This is a usage-order error, not a network failure.

Source

Thrown at agent/src/channels/whatsapp.py:394

            try:
                exc = task.exception()
            except asyncio.CancelledError:
                return
            if login_result.done():
                return
            if exc is not None:
                login_result.set_exception(exc)
            else:
                login_result.set_exception(
                    RuntimeError("WhatsApp connection ended before login completed")
                )

        connect_task.add_done_callback(_on_done)

    async def send(self, msg: OutboundMessage) -> None:
        client = self._client
        if client is None or not self._connected:
            raise RuntimeError("WhatsApp channel is not connected")

        to = self._build_jid(msg.chat_id)
        if msg.content:
            await client.send_message(to, msg.content)

        for media_path in msg.media or []:
            await self._send_media(client, to, media_path)

    def _build_jid(self, raw: str) -> Any:
        api = _load_neonize()
        target = raw.strip()
        match = _JID_RE.match(_normalize_jid(target))
        if not match:
            return api.build_jid(target)

        user = match.group("user").split(":", 1)[0]
        server = match.group("server")
        return api.build_jid(user, server)

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Await connect() and confirm the connected event before sending.
  2. If this happens mid-run, check the DisconnectedEv handling and re-establish the session before retrying send().
  3. Buffer outbound messages and flush them once the connected flag is true.

Example fix

# before
await channel.send(msg)  # before connect

# after
await channel.connect()
# wait for _connected / connected event
while not channel.is_connected:
    await asyncio.sleep(0.5)
await channel.send(msg)
Defensive patterns

Strategy: validation

Validate before calling

async def ensure_connected(channel) -> None:
    if not getattr(channel, '_connected', False) or getattr(channel, '_client', None) is None:
        await channel.connect()

Type guard

def is_whatsapp_ready(channel) -> bool:
    return channel._client is not None and channel._connected

Try / catch

try:
    await channel.send(msg)
except RuntimeError as e:
    if 'not connected' in str(e):
        await channel.connect()
        await channel.send(msg)
    else:
        raise

Prevention

When it happens

Trigger: Calling send() before awaiting connect(); calling send() after the DisconnectedEv handler tore down the client; connect task failed via its done-callback but the caller proceeded anyway.

Common situations: Startup races where messages are queued before the WhatsApp session pairs; session dropped (device unlinked, network loss) and outbound messages still flow; reconnect logic not awaited.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/1c64c1d72049a9aa. Report an issue: GitHub.