chenhg5/cc-connect · error
weixin: sendMessage: response json: %w: %s
Error message
weixin: sendMessage: response json: %w: %s
What it means
After sendMessage posts to the WeChat API it decodes the response body into sendMessageResp. This error wraps a json.Unmarshal failure and appends a 256-byte snippet of the raw body. Non-JSON replies (HTML error pages, truncated bodies) trigger it.
Source
Thrown at platform/weixin/client.go:180
func (c *apiClient) sendMessage(ctx context.Context, msg *sendMessageReq) error {
if msg == nil {
return fmt.Errorf("weixin: sendMessage: nil request")
}
msg.BaseInfo = baseInfo{ChannelVersion: channelVersion}
payload, err := json.Marshal(msg)
if err != nil {
return err
}
raw, err := c.post(ctx, "ilink/bot/sendmessage", payload, 0, "sendMessage")
if err != nil {
return err
}
if len(bytes.TrimSpace(raw)) == 0 {
return nil
}
var resp sendMessageResp
if err := json.Unmarshal(raw, &resp); err != nil {
return fmt.Errorf("weixin: sendMessage: response json: %w: %s", err, truncateForLog(raw, 256))
}
if resp.Ret != 0 {
slog.Warn("weixin: sendMessage declined by API",
"ret", resp.Ret, "errcode", resp.Errcode, "errmsg", resp.Errmsg,
"content_len", len(payload))
return fmt.Errorf("weixin: sendMessage: ret=%d errcode=%d errmsg=%s",
resp.Ret, resp.Errcode, resp.Errmsg)
}
return nil
}
func (c *apiClient) getUploadURL(ctx context.Context, req getUploadURLRequest) (*getUploadURLResponse, error) {
req.BaseInfo = baseInfo{ChannelVersion: channelVersion}
payload, err := json.Marshal(req)
if err != nil {
return nil, err
}
raw, err := c.post(ctx, "ilink/bot/getuploadurl", payload, 0, "getUploadUrl")View on GitHub (pinned to 4000b2338a)
Solutions
- Inspect the appended raw snippet (%s) to identify what came back
- Retry the send; transient gateway bodies resolve on retry
- Check WeChat iLink API changelog and update sendMessageResp if fields changed
- Bypass/inspect proxies that inject HTML into responses
Defensive patterns
Strategy: try-catch
Validate before calling
if len(bytes.TrimSpace(raw)) == 0 { return nil } // already handled; check snippet in error for HTML Type guard
func looksLikeHTML(b []byte) bool { return bytes.Contains(bytes.ToLower(b[:min(64, len(b))]), []byte("<html")) } Try / catch
if err := c.sendMessage(ctx, msg); err != nil {
if strings.Contains(err.Error(), "response json") {
slog.Warn("weixin: non-JSON send response; will retry", "err", err)
time.Sleep(backoff)
return retrySend()
}
return err
} Prevention
- Retry transient non-JSON responses with backoff
- Monitor the raw snippet for gateway/HTML patterns
- Keep sendMessageResp in sync with API changelog
When it happens
Trigger: The send endpoint returns a non-JSON or truncated body: gateway 502 HTML, connection cut mid-response, or an iLink API schema change that makes the body fail to decode into sendMessageResp.
Common situations: Corporate proxy returning HTML; WeChat API outage or protocol change; payload so large the response is truncated; running an old client against a new API.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- get_bot_qrcode json: %w
- get_qrcode_status json: %w
- invalid json response: %w
- weixin: getUpdates json: %w
- weixin: getUploadUrl json: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/5e22e6f1e82b760f.
Report an issue: GitHub.