chenhg5/cc-connect · error

qqbot: SendFile: invalid reply context type %T

Error message

qqbot: SendFile: invalid reply context type %T

What it means

SendFile implements core.FileSender and expects the replyCtx argument to be the platform-internal *replyContext produced when the platform handed back a reply handle. If the caller passes any other type, this error reports the actual dynamic type via %T. It is a programming/contract error, not a runtime condition — legitimate users only obtain replyCtx from this platform's own reply flow.

Source

Thrown at platform/qqbot/qqbot.go:391

		if err := json.NewDecoder(resp.Body).Decode(result); err != nil {
			return fmt.Errorf("qqbot: decode response: %w", err)
		}
	}
	return nil
}

var _ core.ImageSender = (*Platform)(nil)

// buttonDataPrefix is the prefix for QQ Bot keyboard button_data values.
// Format: perm:<decision>:<session_key>
const buttonDataPrefix = "perm:"

// SendFile uploads and sends a file via QQ Bot rich media API.
// Implements core.FileSender.
func (p *Platform) SendFile(ctx context.Context, replyCtx any, file core.FileAttachment) error {
	rctx, ok := replyCtx.(*replyContext)
	if !ok {
		return fmt.Errorf("qqbot: SendFile: invalid reply context type %T", replyCtx)
	}

	fileInfo, err := p.uploadRichMedia(rctx, 4, file.Data, file.FileName)
	if err != nil {
		return fmt.Errorf("qqbot: upload file: %w", err)
	}

	var url string
	switch rctx.messageType {
	case "group":
		url = fmt.Sprintf("%s/v2/groups/%s/messages", p.apiBase(), rctx.groupOpenID)
	case "c2c":
		url = fmt.Sprintf("%s/v2/users/%s/messages", p.apiBase(), rctx.userOpenID)
	default:
		return fmt.Errorf("qqbot: unknown message type %q", rctx.messageType)
	}

	body := map[string]any{

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Only pass a replyCtx obtained from the same qqbot platform instance's reply flow.
  2. Check which platform produced the reply context — contexts are not interchangeable between platforms.
  3. Add a type assertion/guard before calling SendFile if the context origin is dynamic.
  4. If nil, the %T output will show <nil> — trace why no valid reply context was created upstream.

Example fix

// before
err := qqbotPlatform.SendFile(ctx, someOtherPlatformCtx, file)
// after
rctx, ok := replyCtx.(*qqbot.ReplyContext)
if !ok {
    return fmt.Errorf("SendFile: not a qqbot reply context")
}
err := qqbotPlatform.SendFile(ctx, rctx, file)
Defensive patterns

Strategy: type-guard

Validate before calling

rctx, ok := replyCtx.(*qqbot.ReplyContext)
if !ok {
    return fmt.Errorf("SendFile requires a qqbot reply context, got %T", replyCtx)
}

Type guard

func isQQBotReplyContext(ctx any) bool {
    _, ok := ctx.(*qqbot.ReplyContext)
    return ok
}

Try / catch

if err := p.SendFile(ctx, replyCtx, file); err != nil {
    if strings.Contains(err.Error(), "invalid reply context type") {
        return fmt.Errorf("wrong platform context for qqbot SendFile: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: SendFile is called with a replyCtx that is not *qqbot.replyContext — e.g. a reply context created by a different platform (feishu, telegram), a nil value of another type, or a hand-constructed struct.

Common situations: Mixing platform adapters (passing a feishu reply context to the qqbot platform); caching reply contexts across platform restarts where the type changed; test code constructing a fake context instead of using the platform's own.

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/cf05277551e8d9fe. Report an issue: GitHub.