{"record":{"id":"5f0db59c4fab1c52","repo":"chenhg5/cc-connect","slug":"max-upload-file-w","errorCode":null,"errorMessage":"max: upload file: %w","messagePattern":"max: upload file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/max/max.go","lineNumber":458,"sourceCode":"\trctx, ok := replyCtx.(replyContext)\n\tif !ok {\n\t\treturn fmt.Errorf(\"max: unexpected replyCtx type %T\", replyCtx)\n\t}\n\tkind := \"file\"\n\tattType := \"file\"\n\tif strings.HasPrefix(file.MimeType, \"image/\") {\n\t\tkind = \"image\"\n\t\tattType = \"image\"\n\t} else if strings.HasPrefix(file.MimeType, \"video/\") {\n\t\tkind = \"video\"\n\t\tattType = \"video\"\n\t} else if strings.HasPrefix(file.MimeType, \"audio/\") {\n\t\tkind = \"audio\"\n\t\tattType = \"audio\"\n\t}\n\ttoken, err := p.uploadAttachment(ctx, kind, file.Data, file.FileName)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"max: upload file: %w\", err)\n\t}\n\tbody := &maxSendBody{\n\t\tAttachments: []maxOutAttachment{{\n\t\t\tType:    attType,\n\t\t\tPayload: maxTokenPayload{Token: token},\n\t\t}},\n\t}\n\treturn p.postMessage(ctx, rctx.chatID, body)\n}\n\n// SendAudio implements core.AudioSender — uploads a voice/audio blob and sends\n// it as a native MAX audio attachment. Used by the TTS pipeline to reply in\n// voice when [tts] is enabled in config.\nfunc (p *Platform) SendAudio(ctx context.Context, replyCtx any, audio []byte, format string) error {\n\trctx, ok := replyCtx.(replyContext)\n\tif !ok {\n\t\treturn fmt.Errorf(\"max: unexpected replyCtx type %T\", replyCtx)\n\t}","sourceCodeStart":440,"sourceCodeEnd":476,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/max/max.go#L440-L476","documentation":"Wrapped failure from uploadAttachment when sending a file (kind derived from MIME type as file/image/video/audio): the attachment upload failed, so no token is available for the send body.","triggerScenarios":"SendFile called with empty or oversized file.Data, a name without extension, network failure to the media endpoint, or HTTP >= 300 from the upload API (401 invalid token, 413 too large, 415 unsupported kind).","commonSituations":"Token expiry, files above MAX's upload limit, audio/image files whose MIME hint picks an upload kind the API rejects, flaky network to the media host.","solutions":["Read the wrapped error's status: 401/403 → token; 413 → reduce file size; 415 → unsupported type.","Confirm file.Data is complete and file.FileName includes a valid extension.","Retry transient network/5xx failures; nothing was sent yet so retries are safe.","Check MimeType so images/audio are uploaded with the correct kind for previews."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"if len(file.Data) == 0 { return errors.New(\"empty file data\") }\nif file.FileName == \"\" || filepath.Ext(file.FileName) == \"\" { return errors.New(\"file name missing extension\") }","typeGuard":null,"tryCatchPattern":"if err := p.SendFile(ctx, replyCtx, file); err != nil {\n    if strings.Contains(err.Error(), \"upload file\") {\n        if isRetryable(err) {\n            retryWithBackoff(func() error { return p.SendFile(ctx, replyCtx, file) })\n        } else {\n            slog.Error(\"file upload rejected\", \"err\", err)\n        }\n    }\n}","preventionTips":["Check file size against the platform limit before sending","Provide accurate MimeType so the correct upload kind is used","Include valid extensions in file names","Retry only transient (network/5xx) failures"],"tags":["go","upload","http"],"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"}