Skip to content

fix(weixin): treat empty rate-limit message as stale session - #18105

Open
liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:fix/issue-18100-weixin-empty-stale-session
Open

liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:fix/issue-18100-weixin-empty-stale-session

Conversation

@liuhao1024

@liuhao1024 liuhao1024 commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

  • Treat iLink ret=-2 / errcode=-2 with an empty or missing errmsg as a stale Weixin session signal.
  • Keep populated rate-limit messages on the existing rate-limit/backoff path.
  • Extend the Weixin stale-session helper regression tests for empty/None errmsg on both ret and errcode variants.

Related Issue

N/A

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • See commit messages for detailed changes

How to Test

  1. Run pytest tests/ -q — all tests should pass
  2. Verify the specific scenario described above is resolved

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 26.4.1

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture and workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A

@alt-glitch alt-glitch added type/bug Something isn't working comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists labels Apr 30, 2026
@liuhao1024 liuhao1024 closed this May 2, 2026
@liuhao1024 liuhao1024 reopened this May 2, 2026
@XIYBHK

XIYBHK commented May 7, 2026

Copy link
Copy Markdown

Arriving here from #20797 (duplicate, now closed). Two things I'd like to contribute so they don't get lost:

1. Real-world repro log for the PR description / commit trailer

Captured on prod before the fix — shows the exact symptom this patch cures (adapter burning retries against a dead context_token because ret=-2 errmsg=None fell through to the rate-limit branch):

WARNING  gateway.platforms.weixin: [Weixin] rate limited for o9cq80_r; backing off 3.0s before retry
WARNING  gateway.platforms.weixin: [Weixin] rate limited for o9cq80_r; backing off 3.0s before retry
WARNING  gateway.platforms.weixin: [Weixin] rate limited for o9cq80_r; backing off 3.0s before retry
ERROR    gateway.platforms.weixin: [Weixin] send failed to=o9cq80_r: iLink sendmessage rate limited: ret=-2 errcode=None errmsg=None

After a user-initiated inbound message refreshed the session, the next send went through immediately — confirming stale-session, not a genuine frequency cap.

2. Tiny test-symmetry nit

test_errcode_minus_2_with_no_errmsg_is_stale only asserts the None case, while its sibling test_ret_minus_2_with_no_errmsg_is_stale asserts both None and "". Suggest mirroring to keep the two code paths symmetrical:

def test_errcode_minus_2_with_no_errmsg_is_stale(self):
    assert weixin._is_stale_session_ret(None, -2, None) is True
    assert weixin._is_stale_session_ret(None, -2, "") is True

LGTM otherwise — happy to see this land.

@liuhao1024

Copy link
Copy Markdown
Contributor Author

@XIYBHK Thanks for the detailed repro log — great to have real-world confirmation that ret=-2 errmsg=None is indeed stale-session behavior.

On the test symmetry nit: the current diff actually does assert both None and "" in test_errcode_minus_2_with_no_errmsg_is_stale:

def test_errcode_minus_2_with_no_errmsg_is_stale(self):
    assert weixin._is_stale_session_ret(None, -2, None) is True
    assert weixin._is_stale_session_ret(None, -2, "") is True

So the two test methods are already symmetric. Appreciate the careful review though!

rzbdz added a commit to rzbdz/hermes-agent that referenced this pull request May 14, 2026
…tream NousResearch#18105)

iLink returns ret=-2 with errmsg=None or '' for stale context_token,
not just 'unknown error'. The current check missed this variant and
burned all retries against the dead token.

Fix: treat empty/None errmsg as stale-session signal (same as
'unknown error'). Genuine rate limits carry a populated errmsg
('frequency limit' / 'too frequently' / similar).

Ported from NousResearch#18105.
@XIYBHK

XIYBHK commented May 14, 2026

Copy link
Copy Markdown

Hi @teknium1 — hope you're well! Would you be able to take a look at this one when you get a chance?

It's a minimal fix (16 lines changed, 2 files) with full regression coverage. We hit this issue again in production today — the session expired; retrying without context_token line confirms the patch logic is correct, but until it lands upstream we have to maintain a local apply script after every hermes update.

No rush at all, just wanted to make sure it wasn't lost in the queue. Happy to rebase or adjust anything if needed. Thanks so much! 🙏

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for the focused regression fix. The current helper still returns False for empty or missing messages (gateway/platforms/weixin.py:99-107), and the outbound path therefore reaches rate-limit backoff instead of the tokenless retry (gateway/platforms/weixin.py:1771-1813).

Problems

  • The new classifier receives only errmsg, but the existing rate-limit path treats msg as an alternate populated message (gateway/platforms/weixin.py:1795). A response with errmsg=None and msg="frequency limit" would become stale-session handling rather than remain on the rate-limit path.

Suggested changes

  • Normalize errmsg/msg before both _is_stale_session_ret calls (gateway/platforms/weixin.py:1360, gateway/platforms/weixin.py:1775), then add an outbound regression test for tokenless retry and an alternate-msg rate-limit case.

Automated hermes-sweeper review.

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please ensure callers pass the same errmsg/msg fallback used by the existing rate-limit path. Otherwise errmsg=None, msg="frequency limit" is classified as stale despite a populated rate-limit message.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform 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.

4 participants