chenhg5/cc-connect · error
wecom-ws: ack timeout waiting for %s
Error message
wecom-ws: ack timeout waiting for %s
What it means
Timeout sentinel surfacing from writeAndWaitAckStrict: the frame with the given req_id was written to the WeCom WebSocket but no server ack arrived within the configured timeout (errWSAckTimeout from writeAndWaitResult). Unlike the non-strict writeAndWaitAck variant, which logs and proceeds, the strict path — used by sendWSMediaMessage where the ack carries the operation result — returns the timeout as a hard error.
Source
Thrown at platform/wecom/websocket.go:593
return p.writeAndWaitAckWithTimeout(ctx, frame, reqID, wsAckTimeout)
}
func (p *WSPlatform) writeAndWaitAckWithTimeout(ctx context.Context, frame map[string]any, reqID string, timeout time.Duration) error {
result, err := p.writeAndWaitResult(ctx, frame, reqID, timeout)
if errors.Is(err, errWSAckTimeout) {
slog.Debug("wecom-ws: ack timeout, proceeding", "req_id", reqID)
return nil
}
if err != nil {
return err
}
return result.err
}
func (p *WSPlatform) writeAndWaitAckStrict(ctx context.Context, frame map[string]any, reqID string, timeout time.Duration) error {
result, err := p.writeAndWaitResult(ctx, frame, reqID, timeout)
if errors.Is(err, errWSAckTimeout) {
return fmt.Errorf("wecom-ws: ack timeout waiting for %s", reqID)
}
if err != nil {
return err
}
return result.err
}
func (p *WSPlatform) writeAndWaitFrameWithTimeout(ctx context.Context, frame map[string]any, reqID string, timeout time.Duration) (wsFrame, error) {
result, err := p.writeAndWaitResult(ctx, frame, reqID, timeout)
if errors.Is(err, errWSAckTimeout) {
return wsFrame{}, fmt.Errorf("wecom-ws: ack timeout waiting for %s", reqID)
}
if err != nil {
return wsFrame{}, err
}
if result.err != nil {
return wsFrame{}, result.err
}View on GitHub (pinned to 4000b2338a)
Solutions
- Retry the media upload once: transient ack loss is common on the long-lived socket
- Verify network health and WebSocket keepalive/heartbeat cadence if timeouts recur
- Consider whether the strict variant is required — the lenient variant already proceeds on timeout for text frames
- Log req_id (already included) to correlate with any late ack and avoid double-sends on manual retry
Defensive patterns
Strategy: retry
When it happens
Trigger: Thrown at platform/wecom/websocket.go:593 when the library encounters an invalid state.
Common situations: See trigger scenarios.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/4f508c7edd543313.
Report an issue: GitHub.