chenhg5/cc-connect · error

qq: invalid reply context

Error message

qq: invalid reply context

What it means

Returned by Platform.Send when the replyCtx argument is not a *replyContext. The Send API accepts an opaque `any` reply context that must be the internal *replyContext type produced by this platform when it received a message; any other value (nil, a string chat ID, a context from another platform) fails the type assertion.

Source

Thrown at platform/qq/qq.go:449

		// raw_message fallback (string with CQ codes)
		if raw, ok := payload["raw_message"].(string); ok {
			textParts = append(textParts, stripCQCodes(raw))
		}
	}

	return strings.TrimSpace(strings.Join(textParts, "")), images, files, audio
}

// Reply sends a message as a reply to an incoming message.
func (p *Platform) Reply(ctx context.Context, replyCtx any, content string) error {
	return p.Send(ctx, replyCtx, content)
}

// Send sends a message to the conversation identified by replyCtx.
func (p *Platform) Send(ctx context.Context, replyCtx any, content string) error {
	rctx, ok := replyCtx.(*replyContext)
	if !ok {
		return fmt.Errorf("qq: invalid reply context")
	}

	params := map[string]any{
		"message": content,
	}

	if rctx.messageType == "group" {
		params["group_id"] = rctx.groupID
		_, err := p.callAPI("send_group_msg", params)
		return err
	}

	params["user_id"] = rctx.userID
	_, err := p.callAPI("send_private_msg", params)
	return err
}

// SendImage sends an image to the conversation.

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Pass the replyCtx value exactly as delivered by the platform's inbound message event — do not rebuild it.
  2. Ensure the value is *qq.replyContext (pointer, from the qq package), not a copy or an interface re-wrap.
  3. Do not serialize reply contexts across processes; if you must persist routing info, store your own keys and re-resolve via the platform.
  4. Verify you are calling Send on the same Platform instance that produced the reply context.

Example fix

// before
err := platform.Send(ctx, "123456", "hello") // plain string ID

// after
err := platform.Send(ctx, msg.ReplyCtx(), "hello") // ctx delivered with the inbound message
Defensive patterns

Strategy: type-guard

Type guard

func isQQReplyContext(v any) bool {
    _, ok := v.(*replyContext)
    return ok
}

Try / catch

if err := p.Send(ctx, replyCtx, content); err != nil {
    if strings.Contains(err.Error(), "invalid reply context") {
        slog.Error("replyCtx lost or wrong type", "type", fmt.Sprintf("%T", replyCtx))
    }
    return err
}

Prevention

When it happens

Trigger: Calling Send(ctx, replyCtx, content) with replyCtx that is nil, a plain string/int64 conversation ID, a *replyContext value (not pointer), or a reply context obtained from a different platform adapter.

Common situations: Persisting a reply context to JSON and passing the untyped result back; mixing platform adapters (using a Feishu reply context with the QQ platform); hand-crafting a reply context instead of using the one delivered with the inbound message; serialization round-trip that lost the concrete pointer type.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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