Skip to content

fix(weixin): unrecoverable prepare failed is a session error, not a rate limit that opens the breaker (#80125, salvage #80152) - #115359

Merged
kshitijk4poor merged 3 commits into
NousResearch:mainfrom
kshitijk4poor:fix/weixin-prepare-failed-not-rate-limit
Sep 18, 2026
Merged

kshitijk4poor merged 3 commits into
NousResearch:mainfrom
kshitijk4poor:fix/weixin-prepare-failed-not-rate-limit

Conversation

@kshitijk4poor

@kshitijk4poor kshitijk4poor commented Sep 18, 2026 •

Copy link
Copy Markdown

Weixin proactive sends that fail with ret=-2 errmsg=prepare failed (or unknown error) and cannot be recovered are now reported once as a session problem, instead of being retried as a rate limit, opening the 30 s rate-limit breaker and blocking every later send to that account.

Root cause: _send_text_chunk treated every non-zero ret that survived the stale-session recovery attempt as a rate limit. ret=-2 prepare failed is deterministic (the iLink session for that chat is not ready / the user never opened the conversation), so the retry budget was spent on an error that never clears and the breaker tripped (#80125; #112709 reports the same shape for cron deliveries). #113069 (landed 2026-09-16) added the tokenless re-send; this handles what happens when that re-send also fails or there was no token to drop.

  • gateway/platforms/weixin.py: when the response is a stale-session variant (-2) and there is no context token to drop or the token-less resend already failed, raise iLink sendmessage session not ready: … once and stop; -14 and genuine ret=-13 rate limits keep their existing paths. The text deliberately avoids the rate limit substring classify_send_error keys on, so it lands in the generic lane rather than the rate-limited redelivery lane. Both predicates (_is_session_expired, _is_stale_session_ret) now read the same errmsg/msg value. The media leg (_send_file) raises the same error for the same response; the genuine rate-limit cooldown message carries the raw ret/errcode/errmsg.
  • Tests (1 parametrised over stored-token / no-token, tests/gateway/test_weixin.py): the send fails immediately, the error is not classified rate_limited, the breaker stays closed. Red on origin/main.
  • Docs: website/docs/user-guide/messaging/weixin.md troubleshooting row.

Salvage of #80152 by @x7peeps (2026-08-06, the first PR to fail fast and keep the breaker closed for this response; reconstructed onto current main with their authorship since the retry ladder changed under #113069) with Co-authored-by @JonthanaHanh for #80156 (same change, five minutes later). The follow-up commit (media leg, cooldown fields, test, docs) is mine.

Validation
Live repro probe adapter with a ret=-2 errmsg=prepare failed iLink stub on origin/main → rate limited; cooldown 30s, breaker open; on this branch → iLink sendmessage session not ready: ret=-2 …, breaker closed, next send to the account proceeds
Tests test_weixin.py, test_weixin_typing.py, test_weixin_secret_scope.py — 58 passed
Gate hermes-pr-review 2a/2b/2c + simplify-code (3 reviewers) on the final head; every finding folded

Related open PRs on the same symptom (#91647, #100815, #85735, #80426, #74572, #81734, #96437, #101055) will be closed with credit once this lands; most of them are already covered by #113069.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery platform/wecom WeCom / WeChat Work adapter sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Sep 18, 2026
@alt-glitch

Copy link
Copy Markdown

This was generated by AI during triage.

Related: #91647 classifies prepare-failed responses for stale-session recovery; this follow-up handles the no-token or post-retry failure branch without falsely opening the rate-limit breaker. Both address the #80125 family.

x7peeps and others added 2 commits September 19, 2026 02:30
…ion error, not a rate limit (NousResearch#80125)

iLink answers a bot-initiated send with ret=-2 errmsg="prepare failed" when the
peer's session is not ready (no inbound message yet, or the context token is
gone). That is deterministic: treating it as a frequency limit spent the retry
budget on an error that never clears and opened the 30 s rate-limit breaker for
the whole account, blocking every later send (NousResearch#80125, NousResearch#112709).

After the tokenless re-send (46ab37c) has been tried — or when there is no
token to drop — a stale-session -2 now fails fast with a descriptive
"session not ready" error and never reaches the rate-limit path. The text
avoids the "rate limit" substring classify_send_error keys on.

Salvage of NousResearch#80152 (fail fast + keep the breaker closed); NousResearch#80156 arrived five
minutes later with the same change.

Co-authored-by: RelaxJonh <92573950+RelaxJonh@users.noreply.github.com>
…message keeps the raw fields; test + docs

_send_file hit the identical stale -2 after its own tokenless re-send and still
raised the generic "sendmessage error"; it now raises the shared
_session_not_ready_error. The genuine rate-limit cooldown message carries
ret/errcode/errmsg so operators can tell a real -2 frequency limit from the
session case. One parametrised test (stored token / no token) asserts the send
fails once, classify_send_error does not call it rate_limited, and the breaker
stays closed; troubleshooting row in the Weixin docs.
@kshitijk4poor
kshitijk4poor force-pushed the fix/weixin-prepare-failed-not-rate-limit branch from 60494d8 to 0245267 Compare September 18, 2026 21:28
@kshitijk4poor kshitijk4poor changed the title fix(weixin): unrecoverable prepare failed is a session error, not a rate limit that opens the breaker (#80125) fix(weixin): unrecoverable prepare failed is a session error, not a rate limit that opens the breaker (#80125, salvage #80152) Sep 18, 2026
@kshitijk4poor
kshitijk4poor enabled auto-merge (rebase) September 18, 2026 21:45
@kshitijk4poor
kshitijk4poor merged commit 5db9f02 into NousResearch:main Sep 18, 2026
34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/wecom WeCom / WeChat Work adapter sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants