{"record":{"id":"ab87bc766b98ffed","repo":"chenhg5/cc-connect","slug":"download-returned-status-d","errorCode":null,"errorMessage":"download returned status %d","messagePattern":"download returned status (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/dingtalk/dingtalk.go","lineNumber":657,"sourceCode":"\tp.handler(p, msg)\n}\n\nfunc (p *Platform) downloadAudio(downloadCode string) ([]byte, string, error) {\n\t// Get download URL\n\tdownloadURL, err := p.getDownloadURL(downloadCode)\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"get download URL: %w\", err)\n\t}\n\n\t// Download audio file\n\tresp, err := p.httpClient.Get(downloadURL)\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"http get: %w\", err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, \"\", fmt.Errorf(\"download returned status %d\", resp.StatusCode)\n\t}\n\n\tdata, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"read response: %w\", err)\n\t}\n\n\t// Determine MIME type from Content-Type header\n\tmimeType := resp.Header.Get(\"Content-Type\")\n\tif mimeType == \"\" {\n\t\tmimeType = \"audio/amr\" // Default to AMR if not specified\n\t}\n\n\treturn data, mimeType, nil\n}\n\nfunc (p *Platform) getDownloadURL(downloadCode string) (string, error) {\n\ttoken, err := p.getAccessToken()","sourceCodeStart":639,"sourceCodeEnd":675,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/dingtalk/dingtalk.go#L639-L675","documentation":"The DingTalk audio download request to the resolved download URL completed but returned a non-200 HTTP status, so no audio bytes are read; typically an expired download code, auth failure, or CDN error page.","triggerScenarios":"DingTalk's file CDN or an intermediary returned 403/404/410/etc. for the downloadURL during p.httpClient.Get in downloadAudio.","commonSituations":"Expired download URL (very common — DingTalk URLs are short-lived); downloadCode was reused after its URL expired; permission/robot mismatch so the CDN refuses the download; CDN transient 5xx.","solutions":["Obtain a fresh download URL immediately before downloading instead of caching it across messages.","Check the status code in the error to distinguish 403 (auth/permission) from 404/410 (expired URL).","Verify robotCode matches the robot that received the message so the CDN authorizes the download.","Retry once on 5xx; treat 4xx as non-retryable."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// No pre-call API check possible; guard by freshness instead:\n// only call downloadAudio with a downloadCode fetched in the current message handler,\n// never one persisted from an earlier message.","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"download returned status\") {\n    switch {\n    case strings.Contains(err.Error(), \"403\"):\n        // permission/robot mismatch: fix robotCode, do not retry\n    default:\n        // expired URL or transient 5xx: fetch a fresh URL and retry once\n    }\n}","preventionTips":["Never cache download URLs; resolve them per message","Ensure robotCode matches the receiving robot","Retry only 5xx/expired-URL statuses; treat 4xx as permanent"],"tags":["dingtalk","audio","http","status-code"],"backgroundTag":"http-non-2xx-response","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"}