chenhg5/cc-connect · warning
wecom-ws: ack timeout
Error message
wecom-ws: ack timeout
What it means
errWSAckTimeout is the sentinel for the WeCom (WeChat Work) websocket client: after sending a frame with a req_id, no ack/result frame arrived within the timeout (5s normal, 30s media). It is treated as a soft failure — writeAndWaitAckWithTimeout logs at debug and returns nil, proceeding without confirmation, while strict variants convert it into a descriptive error for the caller.
Source
Thrown at platform/wecom/websocket.go:47
secret string
allowFrom string
conn *websocket.Conn
handler core.MessageHandler
ctx context.Context
cancel context.CancelFunc
mu sync.Mutex // protects conn writes
dedup core.MessageDedup
reqSeq atomic.Int64 // monotonic counter for generating unique req_id
missedPong atomic.Int32 // consecutive heartbeat acks not received
pendingAcks sync.Map // req_id -> chan wsAckResult, for sequential send with ack waiting
}
const (
wsAckTimeout = 5 * time.Second
wsMediaAckTimeout = 30 * time.Second
)
var errWSAckTimeout = errors.New("wecom-ws: ack timeout")
// wsReplyContext holds the context needed to reply to a specific message.
type wsReplyContext struct {
reqID string // req_id from headers of aibot_msg_callback
chatID string // chatid for aibot_send_msg
chatType string // chattype: "single" or "group"
userID string // from.userid
}
// --- WebSocket protocol frame types (matching official SDK) ---
// wsFrame is the unified frame structure used for all WebSocket communication.
// Format: { cmd, headers: { req_id }, body: {...} }
// Response frames may omit cmd and include errcode/errmsg instead.
type wsFrame struct {
Cmd string `json:"cmd,omitempty"`
Headers wsFrameHeaders `json:"headers"`
Body json.RawMessage `json:"body,omitempty"`View on GitHub (pinned to 4000b2338a)
Solutions
- Check the debug log line 'wecom-ws: ack timeout, proceeding' frequency — if messages are being lost, treat it as a real delivery problem and inspect the WeCom service status.
- Verify network stability to the WeCom gateway (persistent websocket, no aggressive NAT/firewall idle timeouts); enable keepalive/ping.
- Increase wsAckTimeout/wsMediaAckTimeout if your uplink is slow, especially for media sends.
- If strict mode reports it, retry the send with a fresh req_id after confirming the connection is alive (or reconnect first).
Example fix
// before
if err := p.writeAndWaitAckStrict(ctx, frame, reqID, wsAckTimeout); err != nil { return err }
// after
if err := p.writeAndWaitAckStrict(ctx, frame, reqID, wsAckTimeout); err != nil {
if errors.Is(err, errWSAckTimeout) {
p.reconnectIfNeeded()
return p.writeAndWaitAckStrict(ctx, frame, reqID, wsAckTimeout)
}
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
// before sending, confirm the websocket is still alive:
if p.conn == nil || time.Since(p.lastPong) > 2*wsAckTimeout {
if err := p.reconnect(ctx); err != nil {
return fmt.Errorf("wecom-ws: connection not ready: %w", err)
}
} Try / catch
err := p.writeAndWaitAckStrict(ctx, frame, reqID, wsAckTimeout)
if errors.Is(err, errWSAckTimeout) {
slog.Warn("wecom-ws: ack not received", "req_id", reqID)
p.reconnectIfNeeded()
// retry once with a fresh req_id
return p.writeAndWaitAckStrict(ctx, frame, newReqID(), wsAckTimeout)
} Prevention
- Keep the websocket alive with periodic pings and detect dead connections early
- Size wsAckTimeout/wsMediaAckTimeout to your network; media needs more than 5s
- Monitor debug logs for 'ack timeout, proceeding' frequency as a delivery-health signal
- Reconnect after repeated timeouts instead of continuing to send on a half-dead socket
When it happens
Trigger: writeAndWaitResult times out waiting for the ack keyed by reqID: slow or overloaded WeCom aibot gateway; network drop between client and WeCom; sending frames when the websocket connection is half-dead; media uploads exceeding wsMediaAckTimeout (30s).
Common situations: WeCom service degradation or throttling; corporate firewalls dropping long-lived websocket connections; message bursts where acks are processed out of order or slowly; media (image/file) uploads on slow uplinks.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/025876bf7f269e65.
Report an issue: GitHub.