{"record":{"id":"faaffbcd3118d2f2","repo":"chenhg5/cc-connect","slug":"qqbot-upload-file-w","errorCode":null,"errorMessage":"qqbot: upload file: %w","messagePattern":"qqbot: upload file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/qqbot/qqbot.go","lineNumber":396,"sourceCode":"}\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{\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":378,"sourceCodeEnd":414,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/qqbot/qqbot.go#L378-L414","documentation":"This error wraps any failure from the QQ Bot rich-media upload that happens inside SendFile (platform/qqbot/qqbot.go:396). Before a file can be sent as a media message (msg_type 7), it must be uploaded to QQ's rich media API to obtain a file_info handle; if that upload fails (network error, HTTP error, auth failure), the error is wrapped with 'qqbot: upload file'. It is a transport/API-level failure, not a problem with the reply context itself.","triggerScenarios":"Calling SendFile on a qqbot Platform when uploadRichMedia (media type 4 = file) returns an error: token refresh failure, HTTP >=300 from the rich media endpoint, network timeout, or a decode failure of the upload response.","commonSituations":"Expired/invalid appId or appSecret causing auth failure during upload; oversized or unsupported file; QQ Bot API outage or rate limiting; no network access from the host.","solutions":["Check the wrapped cause (%w) in the error chain to see if it was auth, HTTP status, or network.","Verify appId/appSecret in config.toml are valid QQ Bot credentials by restarting and watching for token refresh errors.","Retry with a smaller file; confirm the file type/size is within QQ Bot rich media limits.","Test outbound connectivity to the QQ Bot API host and check for proxy/firewall interference."],"exampleFix":"// before\nerr := platform.SendFile(ctx, replyCtx, core.FileAttachment{Data: hugeBlob, FileName: \"video.mp4\"})\n// after\nif len(hugeBlob) > maxQQFileSize {\n    return fmt.Errorf(\"file too large for qqbot: %d bytes\", len(hugeBlob))\n}\nif err := platform.SendFile(ctx, replyCtx, core.FileAttachment{Data: hugeBlob, FileName: \"video.mp4\"}); err != nil {\n    slog.Error(\"send file failed\", \"err\", err) // inspect wrapped 'qqbot: upload file' cause\n}","handlingStrategy":"try-catch","validationCode":"if len(file.Data) == 0 || file.FileName == \"\" {\n    return fmt.Errorf(\"refusing to send empty file %q\", file.FileName)\n}","typeGuard":null,"tryCatchPattern":"if err := platform.SendFile(ctx, replyCtx, file); err != nil {\n    var transient = strings.Contains(err.Error(), \"returned 5\") || strings.Contains(err.Error(), \"token request failed\")\n    if transient { /* retry with backoff */ }\n    slog.Error(\"qqbot send file failed\", \"err\", err)\n}","preventionTips":["Validate file data and name before calling SendFile.","Keep appId/appSecret valid so token refresh succeeds during uploads.","Monitor QQ Bot API status for outages before bulk file sends.","Retry transient failures (5xx, network) with exponential backoff."],"tags":["qqbot","file-upload","api","network"],"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-14T05:17:10.506Z"}