HKUDS/Vibe-Trading · error · RuntimeError
getUpdates failed: ret={ret} errcode={errcode} errmsg={data.
Error message
getUpdates failed: ret={ret} errcode={errcode} errmsg={data.get('errmsg', '')} What it means
Raised by the WeChat long-poll loop when the getUpdates-style call returns a non-zero ret or errcode that is not the recognized session-expired code. The library treats session expiry as a pause but surfaces any other poll failure as a RuntimeError including ret, errcode, and errmsg from the API response.
Source
Thrown at agent/src/channels/weixin.py:571
data = await self._api_post("ilink/bot/getupdates", body)
# Check for API-level errors (monitor.ts checks both ret and errcode)
ret = data.get("ret", 0)
errcode = data.get("errcode", 0)
is_error = (ret is not None and ret != 0) or (errcode is not None and errcode != 0)
if is_error:
if errcode == ERRCODE_SESSION_EXPIRED or ret == ERRCODE_SESSION_EXPIRED:
self._pause_session()
remaining = self._session_pause_remaining_s()
self.logger.warning(
"session expired (errcode {}). Pausing {} min.",
errcode,
max((remaining + 59) // 60, 1),
)
return
raise RuntimeError(
f"getUpdates failed: ret={ret} errcode={errcode} errmsg={data.get('errmsg', '')}"
)
# Honour server-suggested poll timeout (monitor.ts:102-105)
server_timeout_ms = data.get("longpolling_timeout_ms")
if server_timeout_ms and server_timeout_ms > 0:
self._next_poll_timeout_s = max(server_timeout_ms // 1000, 5)
# Update cursor
new_buf = data.get("get_updates_buf", "")
if new_buf:
self._get_updates_buf = new_buf
self._save_state()
# Process messages (WeixinMessage[] from types.ts)
msgs: list[dict] = data.get("msgs", []) or []
for msg in msgs:
try:View on GitHub (pinned to 80ffdda44c)
Solutions
- Match errcode/ret against WeChat iLink docs in the error message; auth-related codes mean re-login is required.
- Implement retry with exponential backoff in the poll loop for transient (network/throttle) codes.
- If token invalid, rerun the QR login flow to obtain a fresh session and token.
- Verify the request payload (sync key / offset fields) is consistent between polls.
Example fix
// before
await self._poll_once()
// after
try:
await self._poll_once()
except RuntimeError as e:
self.logger.warning("poll failed, backing off: {}", e)
await asyncio.sleep(min(2 ** failures * 5, 300)) Defensive patterns
Strategy: retry
Try / catch
try:
await channel._poll_once()
except RuntimeError as e:
if "getUpdates failed" in str(e):
await asyncio.sleep(min(2 ** failures * 5, 300))
failures += 1
else:
raise Prevention
- Wrap poll loop in retry with exponential backoff.
- Re-login on auth-related errcodes instead of tight-looping.
- Log ret/errcode/errmsg for correlation with WeChat docs.
When it happens
Trigger: _poll_once (started from start()) receives a poll response where ret != 0 or errcode != 0 and errcode is not ERRCODE_SESSION_EXPIRED — e.g. auth token invalid, malformed sync request, server 5xx disguised as error JSON, or rate limiting.
Common situations: Token revoked or stale after logging in from another device; WeChat server-side changes to the long-poll contract; clock skew or replayed sync keys; heavy polling causing temporary throttling.
Related errors
- Failed to get QR code from WeChat API: {data}
- WeChat send text error (ret={ret}, errcode={errcode}): {data
- getuploadurl returned no upload URL (need upload_full_url or
- WeChat send media error (ret={ret}, errcode={errcode}): {dat
- WeChat session paused, {remaining_min} min remaining (errcod
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/90fe138bd6e85046.
Report an issue: GitHub.