Skip to content

fix(weixin): detect stale context_token when iLink returns "rate limited" (#35949) - #35983

Open
PRATHAMESH75 wants to merge 1 commit into
NousResearch:mainfrom
PRATHAMESH75:fix/weixin-stale-token-rate-limited-35949
Open

fix(weixin): detect stale context_token when iLink returns "rate limited" (#35949)#35983
PRATHAMESH75 wants to merge 1 commit into
NousResearch:mainfrom
PRATHAMESH75:fix/weixin-stale-token-rate-limited-35949

Conversation

@PRATHAMESH75

@PRATHAMESH75 PRATHAMESH75 commented May 31, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #35949 — Weixin iLink stale context_token was misidentified as a rate limit, causing permanent outbound send failure.

iLink's sendmessage endpoint returns ret=-2, errmsg="rate limited" for an expired context_token, not just "unknown error". The stale-session matcher
_is_stale_session_ret only recognised "unknown error", so every outbound message exhausted all 4 retries against the dead token. Only inbound replies survived because they
refresh the token from the incoming webhook.

Fix

case-insensitive comparison preserved).

The send loop at _send_text_chunk (weixin.py:1640) already guards itself with retried_without_token, so the false-positive cost on a genuine rate-limit is capped at one
tokenless retry before falling through to the rate-limit backoff branch. Inbound paths and the -14 SESSION_EXPIRED_ERRCODE path are untouched.

Chose Option B from the issue (expand the matcher) over Option A (treat all ret=-2 as stale) to preserve the existing backoff behavior for genuine throttling.

Test plan

  • New regression test TestIsStaleSessionRet::test_ret_minus_2_with_rate_limited_is_stale covers "rate limited", mixed case, and surrounding whitespace.
  • Existing 7 tests in TestIsStaleSessionRet still pass (no regression on "unknown error", "freq limit", empty errmsg, -14, or success codes).
  • pytest tests/gateway/test_weixin.py::TestIsStaleSessionRet — 8 passed.
  • Manual: confirm stale-token chat now recovers on next send instead of looping with ret=-2.

Workaround (still valid for users on older builds)

Delete the cached token file to force regeneration.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists platform/wecom WeCom / WeChat Work adapter labels May 31, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tracing the stale-token path; current main still has the reported outbound classification gap.

Problems

  • gateway/platforms/weixin.py:99 is shared by both outbound sends and long polling. The proposed "rate limited" match at PR line 113 also feeds getUpdates at gateway/platforms/weixin.py:1360, which treats a match as session expiry and sleeps for 600 seconds at lines 1361-1364. The send-only retried_without_token guard does not protect that path.

Suggested changes

  • Scope the new "rate limited" interpretation to _send_text_chunk_locked (gateway/platforms/weixin.py:1772-1788) so polling retains its existing retry/backoff behavior.
  • Add a send-path regression asserting a "rate limited" response retries once with context_token=None, plus a polling-path regression for the same response.

This is an automated hermes-sweeper review.

if ret != RATE_LIMIT_ERRCODE and errcode != RATE_LIMIT_ERRCODE:
return False
return (errmsg or "").lower() == "unknown error"
return (errmsg or "").strip().lower() in _STALE_SESSION_ERRMSGS

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_is_stale_session_ret is also called by the getUpdates loop. This makes any long-poll ret=-2, errmsg="rate limited" response take the session-expired branch and sleep 600 seconds; the tokenless retry guard cited here exists only in the send path. Please scope this extra phrase to outbound sendmessage handling.

@teknium1 teknium1 added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 13, 2026
…ted"

iLink's sendmessage endpoint returns ret=-2 with errmsg="rate limited"
for an expired context_token, not just "unknown error". The stale-session
matcher missed this string, so all outbound sends silently exhausted 4
retries against the dead token; only inbound replies (which refresh the
token from the webhook) kept working.

Expand _is_stale_session_ret to also match "rate limited". The send
loop's existing retried_without_token guard bounds the false-positive
cost: a genuine rate-limit at most pays one tokenless retry before
falling through to the rate-limit backoff branch.

Fixes NousResearch#35949

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@PRATHAMESH75
PRATHAMESH75 force-pushed the fix/weixin-stale-token-rate-limited-35949 branch from 745b2d7 to 881946e Compare August 25, 2026 17:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Medium — degraded but workaround exists platform/wecom WeCom / WeChat Work adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Weixin iLink: stale context_token misidentified as rate limit, causing permanent send failure

3 participants