{"record":{"id":"1c64c1d72049a9aa","repo":"HKUDS/Vibe-Trading","slug":"whatsapp-channel-is-not-connected","errorCode":null,"errorMessage":"WhatsApp channel is not connected","messagePattern":"WhatsApp channel is not connected","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"agent/src/channels/whatsapp.py","lineNumber":394,"sourceCode":"            try:\n                exc = task.exception()\n            except asyncio.CancelledError:\n                return\n            if login_result.done():\n                return\n            if exc is not None:\n                login_result.set_exception(exc)\n            else:\n                login_result.set_exception(\n                    RuntimeError(\"WhatsApp connection ended before login completed\")\n                )\n\n        connect_task.add_done_callback(_on_done)\n\n    async def send(self, msg: OutboundMessage) -> None:\n        client = self._client\n        if client is None or not self._connected:\n            raise RuntimeError(\"WhatsApp channel is not connected\")\n\n        to = self._build_jid(msg.chat_id)\n        if msg.content:\n            await client.send_message(to, msg.content)\n\n        for media_path in msg.media or []:\n            await self._send_media(client, to, media_path)\n\n    def _build_jid(self, raw: str) -> Any:\n        api = _load_neonize()\n        target = raw.strip()\n        match = _JID_RE.match(_normalize_jid(target))\n        if not match:\n            return api.build_jid(target)\n\n        user = match.group(\"user\").split(\":\", 1)[0]\n        server = match.group(\"server\")\n        return api.build_jid(user, server)","sourceCodeStart":376,"sourceCodeEnd":412,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/channels/whatsapp.py#L376-L412","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Await connect() and confirm the connected event before sending.","If this happens mid-run, check the DisconnectedEv handling and re-establish the session before retrying send().","Buffer outbound messages and flush them once the connected flag is true."],"exampleFix":"# before\nawait channel.send(msg)  # before connect\n\n# after\nawait channel.connect()\n# wait for _connected / connected event\nwhile not channel.is_connected:\n    await asyncio.sleep(0.5)\nawait channel.send(msg)","handlingStrategy":"validation","validationCode":"async def ensure_connected(channel) -> None:\n    if not getattr(channel, '_connected', False) or getattr(channel, '_client', None) is None:\n        await channel.connect()","typeGuard":"def is_whatsapp_ready(channel) -> bool:\n    return channel._client is not None and channel._connected","tryCatchPattern":"try:\n    await channel.send(msg)\nexcept RuntimeError as e:\n    if 'not connected' in str(e):\n        await channel.connect()\n        await channel.send(msg)\n    else:\n        raise","preventionTips":["Always await connect() before the first send","Queue outbound messages and flush on the connected event","Subscribe to DisconnectedEv to pause sending during outages"],"tags":["whatsapp","channel-state","not-connected","messaging"],"backgroundTag":"invalid-channel-state","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}