Skip to content

sendMessage returns ret=-2 with empty errmsg when context_token expires during agent-initiated outbound delivery #225

Description

@umggcsh

Summary

When the OpenClaw agent initiates an outbound message (not as a reply to an inbound WeChat message), sendMessage returns ret=-2 errmsg= with no error description. This happens because the context_token used in the outbound call has expired.

Observed Behavior

  • Inbound reply path (agent responds to a user's WeChat message): works reliably. The context_token from the latest inbound getUpdates response is fresh.
  • Agent-initiated outbound (e.g., cron-triggered news delivery, system-triggered reminders): consistently fails with ret=-2 after ~48 hours of no inbound WeChat messages.

Empirical Timeline

Period Delivery Status Issue
Jul 5 & earlier ❌ silent failure sendMessage returned non-zero but error wasn't parsed
Jul 6–7 mornings ✅ success Fresh context_token after user inbound activity
Jul 7 evenings ❌ ret=-2 Token expired
Jul 9 morning ✅ success User sent inbound message, token refreshed
Jul 10–11 all ❌ ret=-2 No inbound activity → token expired

Pattern: deliveries succeed for ~1 day after the last inbound WeChat message, then fail with ret=-2 until a new inbound message refreshes the token.

Root Cause

The context_token is obtained from inbound getUpdates responses and stored as a per-conversation token. According to the Tencent iLink API protocol, context_token has a limited lifetime (~48 hours based on empirical observation). When it expires, sendMessage returns ret=-2 with an empty errmsg.

The plugin has no mechanism to:

  1. Detect that ret=-2 means "token expired" — the empty errmsg makes it non-obvious vs other ret codes
  2. Refresh the token (e.g., by calling getUpdates or getConfig with no sync cursor to get a fresh context_token)
  3. Fall back to sending without a context_token (which the API may accept for new conversation contexts)
  4. Retry after token refresh

Affected Use Cases

  • Any cron/agent-initiated outbound delivery (scheduled news, reminders, notifications)
  • Messages sent during long periods of user inactivity on WeChat

Proposed Fix Options

Option A: Refresh token on ret=-2 (recommended)

When sendMessage returns ret=-2, the plugin should:

  1. Call getUpdates with an empty sync cursor to obtain a fresh context_token (establishes a new conversation context)
  2. Retry the sendMessage with the new token

Option B: Fall back to no context_token

Test whether omitting context_token from the sendMessage request body causes the API to start a new conversation context. If so, use this as a fallback when the token is stale.

Option C: Proactive token refresh

Track context_token age and proactively refresh it via periodic getUpdates calls when approaching the expiration window (~36 hours).

Environment

Additional Context

The sendMessage response looks like:

ret=-2 errmsg=

Note that errmsg is empty, making the error code the only signal. This contrasts with other errors like errcode=-14 (session timeout) in the getUpdates endpoint which do include a descriptive message.

The context_token is documented in the API protocol:

  • Inbound: included in getUpdates response msg.context_token
  • Outbound: must be passed back in sendMessage request msg.context_token

But its lifetime and failure mode are not documented.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions