chenhg5/cc-connect · error

wecom-ws: invalid reply context type %T

Error message

wecom-ws: invalid reply context type %T

What it means

Type-assertion guard inside wecom-ws Reply: the reply context value passed in by the engine is not a platform/wecom wsReplyContext (the struct carrying req_id/chatID/userID obtained from the original aibot callback). Any other type — including a context reconstructed by a different platform or a nil/anonymous value — cannot be used to address the aibot_respond_msg frame, so the send is refused before any I/O.

Solutions

  1. Ensure only contexts produced by this platform's message callback (or ReconstructReplyCtx) are routed back into Reply
  2. If the context came from another platform, fix the engine's session/platform binding so replies are dispatched to the platform that received the original message
  3. For cron/proactive flows that lack a req_id, call Send (aibot_send_msg) instead of Reply
  4. Add a test asserting the engine never forwards a foreign context type
Defensive patterns

Strategy: type-guard

When it happens

Trigger: Thrown at platform/wecom/websocket.go:470 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/163ae2b4baf2278d. Report an issue: GitHub.

Appendix: source

Thrown at platform/wecom/websocket.go:470

			Content:  content, ExtraContent: quotedContent, ReplyCtx: rctx,
		})
		return
	}

	slog.Info("wecom-ws: media message", "msg_type", body.MsgType, "user", body.From.UserID,
		"images", len(current.images)+len(quoted.images), "files", len(current.files)+len(quoted.files), "text_parts", len(current.content))
	go p.deliverWSMediaInbound(&body, sessionKey, chatName, rctx, current, quoted, false)
}

// Reply sends a response message via aibot_respond_msg using the stream format.
// Uses the req_id from the original callback.
// The stream content field is a full-replacement (not incremental append), so we
// send the complete content in one frame with finish=true.
// Markdown is natively supported by the stream reply format.
func (p *WSPlatform) Reply(ctx context.Context, rctx any, content string) error {
	rc, ok := rctx.(wsReplyContext)
	if !ok {
		return fmt.Errorf("wecom-ws: invalid reply context type %T", rctx)
	}
	if content == "" {
		return nil
	}

	streamID := p.generateReqID("stream")
	frame := map[string]any{
		"cmd":     "aibot_respond_msg",
		"headers": map[string]string{"req_id": rc.reqID},
		"body": map[string]any{
			"msgtype": "stream",
			"stream": map[string]any{
				"id":      streamID,
				"finish":  true,
				"content": content,
			},
		},
	}

View on GitHub (pinned to 4000b2338a)