{"record":{"id":"b6bcfcb00c88ebbe","repo":"chenhg5/cc-connect","slug":"qqbot-upload-image-w","errorCode":null,"errorMessage":"qqbot: upload image: %w","messagePattern":"qqbot: upload image: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/qqbot/qqbot.go","lineNumber":243,"sourceCode":"\tfor _, chunk := range chunks {\n\t\tif err := p.sendMessage(rctx, chunk); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// SendImage uploads and sends an image via QQ Bot rich media API.\n// Implements core.ImageSender.\nfunc (p *Platform) SendImage(ctx context.Context, replyCtx any, img core.ImageAttachment) error {\n\trctx, ok := replyCtx.(*replyContext)\n\tif !ok {\n\t\treturn fmt.Errorf(\"qqbot: SendImage: invalid reply context type %T\", replyCtx)\n\t}\n\n\tfileInfo, err := p.uploadRichMedia(rctx, 1, img.Data, \"\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"qqbot: upload image: %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{\n\t\t\"msg_type\": 7,\n\t\t\"media\":    map[string]any{\"file_info\": fileInfo},\n\t}\n\tif rctx.eventMsgID != \"\" {\n\t\tbody[\"msg_id\"] = rctx.eventMsgID","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/qqbot/qqbot.go#L225-L261","documentation":"After the reply-context assertion succeeds, SendImage() calls uploadRichMedia (file_type 1 = image) to get a file_info token from the QQ Bot rich-media API. Any failure of that HTTP upload is wrapped with this message and returned. The message was never sent; the error reflects the upload step, not the send step.","triggerScenarios":"uploadRichMedia returns an error because the media API returned non-2xx, the base64 payload exceeds size limits, the access token expired mid-request, the media URL for the group/c2c type is wrong, or the network request failed.","commonSituations":"Image larger than QQ Bot's media size limit; expired access token not yet refreshed; QQ media endpoint transient 5xx; sending to a group the bot was removed from (permission error surfaced by the API).","solutions":["Inspect the wrapped inner error (%w) for the API's HTTP status and error code.","Check the image size/format against QQ Bot rich-media limits before calling SendImage.","Ensure the access token is fresh — the platform refreshes it, but long-lived processes may race expiry.","Retry once after a short delay for transient 5xx/network errors.","Verify the bot still has access to the target group/c2c conversation."],"exampleFix":"// before\nif err := p.SendImage(ctx, rctx, img); err != nil { return err }\n// after\nif err := p.SendImage(ctx, rctx, img); err != nil {\n    if strings.Contains(err.Error(), \"upload image\") {\n        slog.Warn(\"media upload failed, retrying\", \"error\", err)\n        time.Sleep(2 * time.Second)\n        return p.SendImage(ctx, rctx, img)\n    }\n    return err\n}","handlingStrategy":"retry","validationCode":"if len(img.Data) == 0 { return errors.New(\"qqbot: refusing to send empty image\") }\nif len(img.Data) > maxMediaBytes { return fmt.Errorf(\"qqbot: image %d bytes exceeds limit\", len(img.Data)) }","typeGuard":null,"tryCatchPattern":"if err := p.SendImage(ctx, replyCtx, img); err != nil {\n    if strings.Contains(err.Error(), \"upload image\") {\n        var apiErr *APIError\n        if errors.As(err, &apiErr) { /* inspect apiErr status/code before retry */ }\n    }\n}","preventionTips":["Pre-check image size and format against QQ rich-media limits.","Refresh the access token on auth-related upload failures.","Add one retry with short backoff for transient 5xx from the media endpoint.","Verify the bot's membership/permission in the target group before media sends."],"tags":["qqbot","http","image-upload","api"],"backgroundTag":"api-request-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}