chenhg5/cc-connect · error

qqbot: empty session key in reply context

Error message

qqbot: empty session key in reply context

What it means

SendWithButtons embeds the session key into every button's button_data so that button-click (INTERACTION_CREATE) callbacks can be routed back to the right session. If the reply context's sessionKey field is empty, the callback could not be routed, so the send is refused with this error (platform/qqbot/qqbot.go:435).

Source

Thrown at platform/qqbot/qqbot.go:435

	return p.apiRequest("POST", url, body)
}

var _ core.FileSender = (*Platform)(nil)
var _ core.InlineButtonSender = (*Platform)(nil)

// SendWithButtons sends a message with QQ Bot inline keyboard buttons.
// Implements core.InlineButtonSender.
func (p *Platform) SendWithButtons(ctx context.Context, replyCtx any, content string, buttons [][]core.ButtonOption) error {
	rctx, ok := replyCtx.(*replyContext)
	if !ok {
		return fmt.Errorf("qqbot: SendWithButtons: invalid reply context type %T", replyCtx)
	}

	// Use session key from replyContext to embed in button_data
	sessionKey := rctx.sessionKey
	if sessionKey == "" {
		return fmt.Errorf("qqbot: empty session key in reply context")
	}

	// Build QQ Bot keyboard rows from button options
	var rows []map[string]any
	for i, row := range buttons {
		var btns []map[string]any
		for j, btn := range row {
			// Encode decision + session key into button_data so we can route
			// the INTERACTION_CREATE event back to the right session.
			// btn.Data is already "perm:allow", "perm:deny", or "perm:allow_all"
			buttonData := btn.Data + ":" + sessionKey

			btnID := fmt.Sprintf("b_%d_%d", i, j)
			visitedLabel := "已操作"
			style := 1 // blue
			if strings.Contains(btn.Data, "deny") {
				visitedLabel = "已拒绝"
				style = 0 // grey

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Always build reply contexts via the qqbot platform's normal message handling or ReconstructReplyCtx, both of which set sessionKey.
  2. If constructing manually, set sessionKey to the full key (e.g. "qqbot:g:<group_openid>").
  3. Audit recent changes to where replyContext is created in qqbot.go for a dropped sessionKey assignment.

Example fix

// before
rctx := &replyContext{messageType: "group", groupOpenID: gid}
sendWithButtons(rctx)
// after
rctx := &replyContext{messageType: "group", groupOpenID: gid, sessionKey: "qqbot:g:" + gid}
sendWithButtons(rctx)
Defensive patterns

Strategy: validation

Validate before calling

if rc, ok := replyCtx.(*replyContext); !ok || rc.sessionKey == "" {
    return fmt.Errorf("reply context missing session key; cannot route button callbacks")
}

Type guard

rc, ok := replyCtx.(*replyContext); ok && rc.sessionKey != ""

Prevention

When it happens

Trigger: Calling SendWithButtons with a *replyContext that was built without setting sessionKey — e.g. a manually constructed context, or code paths that populate messageType/openIDs but skip sessionKey.

Common situations: Hand-constructed replyContext in tests or plugins; a code change that stopped copying sessionKey when creating reply contexts; deserialized/cached contexts missing the field.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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