{"record":{"id":"cf05277551e8d9fe","repo":"chenhg5/cc-connect","slug":"qqbot-sendfile-invalid-reply-context-type-t","errorCode":null,"errorMessage":"qqbot: SendFile: invalid reply context type %T","messagePattern":"qqbot: SendFile: invalid reply context type %T","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/qqbot/qqbot.go","lineNumber":391,"sourceCode":"\t\tif err := json.NewDecoder(resp.Body).Decode(result); err != nil {\n\t\t\treturn fmt.Errorf(\"qqbot: decode response: %w\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\nvar _ core.ImageSender = (*Platform)(nil)\n\n// buttonDataPrefix is the prefix for QQ Bot keyboard button_data values.\n// Format: perm:<decision>:<session_key>\nconst buttonDataPrefix = \"perm:\"\n\n// SendFile uploads and sends a file via QQ Bot rich media API.\n// Implements core.FileSender.\nfunc (p *Platform) SendFile(ctx context.Context, replyCtx any, file core.FileAttachment) error {\n\trctx, ok := replyCtx.(*replyContext)\n\tif !ok {\n\t\treturn fmt.Errorf(\"qqbot: SendFile: invalid reply context type %T\", replyCtx)\n\t}\n\n\tfileInfo, err := p.uploadRichMedia(rctx, 4, file.Data, file.FileName)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"qqbot: upload file: %w\", err)\n\t}\n\n\tvar url string\n\tswitch rctx.messageType {\n\tcase \"group\":\n\t\turl = fmt.Sprintf(\"%s/v2/groups/%s/messages\", p.apiBase(), rctx.groupOpenID)\n\tcase \"c2c\":\n\t\turl = fmt.Sprintf(\"%s/v2/users/%s/messages\", p.apiBase(), rctx.userOpenID)\n\tdefault:\n\t\treturn fmt.Errorf(\"qqbot: unknown message type %q\", rctx.messageType)\n\t}\n\n\tbody := map[string]any{","sourceCodeStart":373,"sourceCodeEnd":409,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/qqbot/qqbot.go#L373-L409","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Only pass a replyCtx obtained from the same qqbot platform instance's reply flow.","Check which platform produced the reply context — contexts are not interchangeable between platforms.","Add a type assertion/guard before calling SendFile if the context origin is dynamic.","If nil, the %T output will show <nil> — trace why no valid reply context was created upstream."],"exampleFix":"// before\nerr := qqbotPlatform.SendFile(ctx, someOtherPlatformCtx, file)\n// after\nrctx, ok := replyCtx.(*qqbot.ReplyContext)\nif !ok {\n    return fmt.Errorf(\"SendFile: not a qqbot reply context\")\n}\nerr := qqbotPlatform.SendFile(ctx, rctx, file)","handlingStrategy":"type-guard","validationCode":"rctx, ok := replyCtx.(*qqbot.ReplyContext)\nif !ok {\n    return fmt.Errorf(\"SendFile requires a qqbot reply context, got %T\", replyCtx)\n}","typeGuard":"func isQQBotReplyContext(ctx any) bool {\n    _, ok := ctx.(*qqbot.ReplyContext)\n    return ok\n}","tryCatchPattern":"if err := p.SendFile(ctx, replyCtx, file); err != nil {\n    if strings.Contains(err.Error(), \"invalid reply context type\") {\n        return fmt.Errorf(\"wrong platform context for qqbot SendFile: %w\", err)\n    }\n    return err\n}","preventionTips":["Only pass reply contexts created by the same qqbot platform instance.","Never share reply contexts between platform adapters.","Guard with a type assertion when the context's origin is dynamic.","In tests, build contexts via the platform's own reply flow, not ad-hoc structs."],"tags":["qqbot","type-error","api-misuse"],"backgroundTag":"type-mismatch","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}