hermes - ✅(Solved) Fix [Weixin] Long text messages (>2000 chars) are truncated without error [3 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#16411Fetched 2026-04-28 06:53:32
View on GitHub
Comments
0
Participants
1
Timeline
7
Reactions
0
Author
Participants
Timeline (top)
labeled ×4cross-referenced ×3

Error Message

Additionally, when the iLink API returns error code -2 (rate limit / message too long), the current retry logic does not recognize this code effectively, causing unnecessary retries that all fail with the same error.

Error Log

WARNING gateway.platforms.weixin: [Weixin] send chunk failed to=wxid_xxx attempt=1/5, retrying in 1.00s: iLink error ret=-2 4. Check ~/.hermes/logs/gateway.error.log for -2 error retries.

Root Cause

In gateway/platforms/weixin.py, MAX_MESSAGE_LENGTH = 2000 is defined but send() does not split long text into chunks before calling _send_message(). The entire content is sent in a single API call, and the iLink backend truncates or rejects it.

The retry loop in _send_text_chunk() only handles SESSION_EXPIRED_ERRCODE (401) and RATE_LIMIT_ERRCODE (-2) at the response level, but the initial send attempt already fails with -2 and gets retried with the same oversized payload.

Fix Action

Fixed

PR fix notes

PR #16422: fix(weixin): fail fast on permanent iLink error codes instead of retrying

Description (problem / solution / changelog)

Summary

  • Adds NON_RETRYABLE_ERRCODES = frozenset({-2}) constant for iLink error codes that cannot be resolved by retrying
  • Introduces _ILinkPermanentError sentinel raised when a permanent error code is received
  • _send_text_chunk now breaks the retry loop immediately on ret=-2 or errcode=-2; transient errors and session-expiry (-14) continue to retry as before

The bug

When iLink returns ret=-2 (invalid payload / message too long), _send_text_chunk caught the resulting RuntimeError in its retry loop and re-sent the identical chunk up to send_chunk_retries times (default 2 retries), producing spurious warning logs and unnecessary latency before the final failure:

WARNING gateway.platforms.weixin: [Weixin] send chunk failed to=wxid_xxx attempt=1/3, retrying in 1.00s: iLink sendmessage error: ret=-2
WARNING gateway.platforms.weixin: [Weixin] send chunk failed to=wxid_xxx attempt=2/3, retrying in 2.00s: iLink sendmessage error: ret=-2

Since ret=-2 indicates a permanent payload rejection (the content itself is invalid or too long for the endpoint), resending the same payload cannot succeed.

The fix

_ILinkPermanentError(RuntimeError) is raised instead of a plain RuntimeError for codes in NON_RETRYABLE_ERRCODES. The except Exception handler checks isinstance(exc, _ILinkPermanentError) and breaks the loop immediately, equivalent to having exhausted all retries.

Test plan

  • Before: simulation confirms old code makes 4 _send_message calls (3 retries) for ret=-2
  • After: test_ret_minus_2_fails_without_retry — exactly 1 call, error raised immediately
  • test_errcode_minus_2_fails_without_retry — same fast-fail via the errcode field
  • test_transient_error_still_retries — transient RuntimeError still triggers the retry path
  • test_send_returns_failure_on_permanent_errorsend() propagates the error correctly
  • All 46 existing tests/gateway/test_weixin.py tests pass unchanged

Related

  • Fixes #16411

🤖 Generated with Claude Code

Changed files

  • gateway/platforms/weixin.py (modified, +16/-4)
  • tests/gateway/test_weixin.py (modified, +82/-0)

PR #16465: fix(weixin): treat ret=-2 as token-invalid for retry

Description (problem / solution / changelog)

Summary

The cached context_token in WeixinAdapter can silently become stale, after which the iLink endpoint rejects sends with ret=-2 (errcode=None, errmsg='unknown error') instead of the documented session-expired code (SESSION_EXPIRED_ERRCODE).

The existing tokenless-retry path already handles the case correctly — strip the cached token and retry once — but it only triggers when ret/errcode matches SESSION_EXPIRED_ERRCODE. As a result ret=-2 falls straight through to a hard send failure.

This widens the trigger to include ret=-2 so the existing recovery logic kicks in.

Reproduction

Observed in production for ~3 days on cron deliveries: a previously-healthy DM target starts returning ret=-2 errcode=None errmsg=unknown error for every push. Restarting the gateway clears the bad token and recovery resumes — confirming the cached token was the cause.

Sample log entries:

ERROR gateway.platforms.weixin: [Weixin] send failed to=...: iLink sendmessage error: ret=-2 errcode=None errmsg=unknown error
ERROR cron.scheduler: Job '...': delivery error: Weixin send failed: ...

Test plan

  • python -m py_compile gateway/platforms/weixin.py passes
  • Locally verified: forcing a stale-token scenario now triggers the existing tokenless retry path and succeeds on the retry
  • Reviewer to confirm no other code paths assume ret=-2 means a different failure mode

Changed files

  • gateway/platforms/weixin.py (modified, +7/-2)

PR #16757: fix(weixin): split long messages (>2000 chars) into chunks to prevent truncation

Description (problem / solution / changelog)

Summary

This PR fixes the Weixin long message truncation bug described in #16411.

Changes

  • MAX_MESSAGE_LENGTH: Reduced from 4000 to 2000 to match the Weixin iLink API's actual message size limit
  • RATE_LIMIT_ERRCODE handling: Added -2 error code detection with 3x backoff retry logic in _send_text_chunk()
  • Default chunk delay: Increased from 0.35s to 1.5s to avoid triggering rate limits between chunks
  • Default retries: Increased from 2 to 4 for better reliability on transient failures
  • Message chunking in send(): Long messages are now split using the existing _split_text() infrastructure before delivery, with configurable delay between chunks

The bug

When sending text messages >2000 characters via the Weixin gateway, content beyond the limit was silently truncated. The recipient only received the first portion with no indication that part of the message was lost.

Additionally, when the iLink API returns error code -2 (rate limit / message too long), the retry logic did not handle this effectively.

Test plan

  • Local testing confirms messages >2000 chars are split and delivered as multiple chunks
  • Rate limit -2 responses trigger backoff before retry
  • Chunk delay of 1.5s prevents rate limit hits during multi-chunk sends

Related

  • Fixes #16411
  • Complements #16422 (fail-fast on permanent iLink error codes)

Changed files

  • gateway/platforms/weixin.py (modified, +17/-3)

Code Example

WARNING gateway.platforms.weixin: [Weixin] send chunk failed to=wxid_xxx attempt=1/5, retrying in 1.00s: iLink error ret=-2

---

chunks = [c for c in self._split_text(self.format_message(final_content)) if c and c.strip()]
for idx, chunk in enumerate(chunks):
    client_id = f"hermes-weixin-{uuid.uuid4().hex}"
    await self._send_text_chunk(
        chat_id=chat_id,
        chunk=chunk,
        context_token=context_token,
        client_id=client_id,
    )
    last_message_id = client_id
    if idx < len(chunks) - 1 and self._send_chunk_delay_seconds > 0:
        await asyncio.sleep(self._send_chunk_delay_seconds)
RAW_BUFFERClick to expand / collapse

Bug Description

When sending long text messages (>2000 characters) via the Weixin gateway, content beyond the 2000-char limit is silently truncated. The recipient only receives the first portion, with no indication that part of the message was lost.

Additionally, when the iLink API returns error code -2 (rate limit / message too long), the current retry logic does not recognize this code effectively, causing unnecessary retries that all fail with the same error.

Error Log

WARNING gateway.platforms.weixin: [Weixin] send chunk failed to=wxid_xxx attempt=1/5, retrying in 1.00s: iLink error ret=-2

Root Cause

In gateway/platforms/weixin.py, MAX_MESSAGE_LENGTH = 2000 is defined but send() does not split long text into chunks before calling _send_message(). The entire content is sent in a single API call, and the iLink backend truncates or rejects it.

The retry loop in _send_text_chunk() only handles SESSION_EXPIRED_ERRCODE (401) and RATE_LIMIT_ERRCODE (-2) at the response level, but the initial send attempt already fails with -2 and gets retried with the same oversized payload.

Reproduction Steps

  1. Start Hermes gateway with Weixin platform enabled.
  2. Trigger a response >2000 characters (e.g., ask the agent to summarize a long document).
  3. Observe that only the first ~2000 characters arrive on Weixin.
  4. Check ~/.hermes/logs/gateway.error.log for -2 error retries.

Proposed Fix

Split long messages into chunks before sending, with a configurable delay between chunks:

chunks = [c for c in self._split_text(self.format_message(final_content)) if c and c.strip()]
for idx, chunk in enumerate(chunks):
    client_id = f"hermes-weixin-{uuid.uuid4().hex}"
    await self._send_text_chunk(
        chat_id=chat_id,
        chunk=chunk,
        context_token=context_token,
        client_id=client_id,
    )
    last_message_id = client_id
    if idx < len(chunks) - 1 and self._send_chunk_delay_seconds > 0:
        await asyncio.sleep(self._send_chunk_delay_seconds)

This leverages the existing _split_text() and _send_text_chunk() infrastructure already used by other platforms (e.g., Discord), and respects the configurable send_chunk_delay_seconds (default 1.5s) to avoid triggering rate limits.

Related

  • #13769 — previous Weixin send_image_file() parameter mismatch fix (closed)

Environment

  • Hermes version: latest main (commit range includes weixin adapter)
  • Platform: macOS (affects all platforms using Weixin gateway)
  • Python: 3.11

extent analysis

TL;DR

Split long text messages into chunks before sending via the Weixin gateway to prevent truncation and implement a retry logic that handles the -2 error code effectively.

Guidance

  • Review the gateway/platforms/weixin.py file to ensure that the send() function splits long text into chunks using the _split_text() method before calling _send_message().
  • Implement a retry logic in _send_text_chunk() that recognizes the -2 error code and handles it accordingly to prevent unnecessary retries.
  • Configure the send_chunk_delay_seconds parameter to introduce a delay between chunk sends and avoid triggering rate limits.
  • Test the changes by sending long text messages (>2000 characters) via the Weixin gateway and verify that the entire message is received by the recipient.

Example

The proposed fix code snippet provided in the issue body demonstrates how to split long messages into chunks and send them with a configurable delay:

chunks = [c for c in self._split_text(self.format_message(final_content)) if c and c.strip()]
for idx, chunk in enumerate(chunks):
    # ...
    if idx < len(chunks) - 1 and self._send_chunk_delay_seconds > 0:
        await asyncio.sleep(self._send_chunk_delay_seconds)

Notes

The fix assumes that the _split_text() and _send_text_chunk() methods are already implemented and working correctly for other platforms. Additionally, the send_chunk_delay_seconds parameter should be configured to a value that avoids triggering rate limits.

Recommendation

Apply the proposed fix to split long text messages into chunks and implement a retry logic that handles the -2 error code effectively, as it addresses the root cause of the issue and provides a configurable delay to avoid rate limits.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix [Weixin] Long text messages (>2000 chars) are truncated without error [3 pull requests, 1 participants]