{"record":{"id":"1ea846923e99946f","repo":"chenhg5/cc-connect","slug":"api-returned-status-d","errorCode":null,"errorMessage":"api returned status %d","messagePattern":"api returned status (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/dingtalk/dingtalk.go","lineNumber":708,"sourceCode":"\tdefer cancel()\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost,\n\t\t\"https://api.dingtalk.com/v1.0/robot/messageFiles/download\",\n\t\tbytes.NewReader(bodyBytes))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"create request: %w\", err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\treq.Header.Set(\"x-acs-dingtalk-access-token\", token)\n\n\tresp, err := p.httpClient.Do(req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"do request: %w\", err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"api returned status %d\", resp.StatusCode)\n\t}\n\n\tvar result downloadResponse\n\tif err := json.NewDecoder(resp.Body).Decode(&result); err != nil {\n\t\treturn \"\", fmt.Errorf(\"decode response: %w\", err)\n\t}\n\n\tif result.DownloadUrl == \"\" {\n\t\treturn \"\", fmt.Errorf(\"empty downloadUrl in response\")\n\t}\n\n\treturn result.DownloadUrl, nil\n}\n\nfunc (p *Platform) getAccessToken() (string, error) {\n\tp.tokenMu.Lock()\n\tdefer p.tokenMu.Unlock()\n","sourceCodeStart":690,"sourceCodeEnd":726,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/dingtalk/dingtalk.go#L690-L726","documentation":"getDownloadURL calls DingTalk's media download API to obtain a temporary download URL for an incoming image/file/audio message. When the API responds with any HTTP status other than 200, the function aborts and wraps the status code in this error. It indicates the download-ticket request itself failed before a URL could be parsed.","triggerScenarios":"Called from handleImageMessage, handleFileMessage, or downloadAudio when the POST to the DingTalk media/download endpoint returns a non-200 status, e.g. 400 for a malformed downloadCode, 401/403 for an expired or invalid access token, 404 for an expired code, or 5xx from DingTalk.","commonSituations":"Expired downloadCode (DingTalk media codes are short-lived and single-use), an access token that expired or was revoked after an app-secret rotation, wrong appKey/appSecret config, or transient DingTalk server errors (500/502/503).","solutions":["Check the status code in the error message: 401/403 means the access token is invalid — verify appKey/appSecret in config.toml and restart.","If the code is 4xx referencing the downloadCode, the media code likely expired; ask the user to resend the image/file.","For 5xx, retry getDownloadURL with backoff after confirming the DingTalk status page shows no outage.","Confirm the robot/app has media download permissions in the DingTalk developer console.","Enable debug logging to capture the full request/response and compare against DingTalk API docs."],"exampleFix":"// before: single attempt, no retry on transient status\nurl, err := p.getDownloadURL(ctx, code)\nif err != nil { return fmt.Errorf(\"dingtalk: get download url: %w\", err) }\n\n// after: retry transient statuses\nurl, err := p.getDownloadURL(ctx, code)\nif err != nil {\n    if strings.Contains(err.Error(), \"status 5\") {\n        time.Sleep(2 * time.Second)\n        url, err = p.getDownloadURL(ctx, code)\n    }\n    if err != nil { return fmt.Errorf(\"dingtalk: get download url: %w\", err) }\n}","handlingStrategy":"retry","validationCode":"if !strings.HasPrefix(mediaCode, \"\") && time.Since(mediaCodeReceivedAt) < 5*time.Minute {\n    // code likely still valid, safe to call\n}","typeGuard":null,"tryCatchPattern":"url, err := p.getDownloadURL(ctx, code)\nif err != nil {\n    var status int\n    if _, serr := fmt.Sscanf(err.Error(), \"api returned status %d\", &status); serr == nil && status >= 500 {\n        time.Sleep(2 * time.Second)\n        url, err = p.getDownloadURL(ctx, code)\n    }\n    if err != nil { return fmt.Errorf(\"download url: %w\", err) }\n}","preventionTips":["Process media messages immediately; DingTalk downloadCodes expire quickly.","Keep appKey/appSecret current and monitor DingTalk status for outages.","Log status codes to distinguish config (4xx) vs outage (5xx) issues.","Refresh the access token before its expiry window (code already caches with 5-minute buffer)."],"tags":["http","dingtalk","network","api"],"backgroundTag":"http-error-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"}