{"record":{"id":"99b41f20cf1ccb82","repo":"chenhg5/cc-connect","slug":"upload-returned-status-d-s","errorCode":null,"errorMessage":"upload returned status %d: %s","messagePattern":"upload returned status (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/dingtalk/dingtalk.go","lineNumber":1382,"sourceCode":"\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"create upload request: %w\", err)\n\t}\n\n\treq.Header.Set(\"Content-Type\", writer.FormDataContentType())\n\n\tresp, err := p.httpClient.Do(req)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"upload request: %w\", err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\trespBody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"read upload response: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"upload returned status %d: %s\", resp.StatusCode, respBody)\n\t}\n\n\tslog.Debug(\"dingtalk: media upload response\", \"status\", resp.StatusCode, \"body\", string(respBody))\n\n\tvar uploadResp struct {\n\t\tErrCode int    `json:\"errcode\"`\n\t\tErrMsg  string `json:\"errmsg\"`\n\t\tMediaID string `json:\"media_id\"`\n\t\tType    string `json:\"type\"`\n\t}\n\tif err := json.Unmarshal(respBody, &uploadResp); err != nil {\n\t\treturn \"\", fmt.Errorf(\"decode upload response: %w, body: %s\", err, respBody)\n\t}\n\n\tif uploadResp.ErrCode != 0 {\n\t\treturn \"\", fmt.Errorf(\"upload API error %d: %s\", uploadResp.ErrCode, uploadResp.ErrMsg)\n\t}\n","sourceCodeStart":1364,"sourceCodeEnd":1400,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/dingtalk/dingtalk.go#L1364-L1400","documentation":"The DingTalk media upload HTTP endpoint responded with a status code other than 200. The error includes both the numeric status and the raw response body so the caller can see DingTalk's own error payload. This is thrown whenever resp.StatusCode != http.StatusOK after a media upload request.","triggerScenarios":"Any media upload where the server returns e.g. 400 (malformed multipart), 401/403 (invalid or expired access_token in the upload URL), 404 (wrong upload endpoint or media type path), or 5xx (DingTalk server-side failure).","commonSituations":"Expired or wrong access token embedded in the upload URL; uploading a media type whose endpoint path is wrong; file rejected server-side; DingTalk API outage or rate limiting returning 5xx/429; corporate proxy returning its own non-200 HTML error page.","solutions":["Inspect the status code and body in the error message; for 401/403 refresh the access token (delete cached token and let getAccessToken re-fetch).","Verify the mediaType matches a supported value (image, voice, video, file) and that the correct upload endpoint URL for that type is used.","Check the request body — multipart form field name ('media'), filename, and Content-Type must be correct for the DingTalk upload API.","If status is 5xx, retry after a short delay; if persistent, check the DingTalk Open Platform status/announcements.","Ensure the app (AgentId/AppKey) has media upload permission enabled in the DingTalk developer console."],"exampleFix":"// before\nif resp.StatusCode != http.StatusOK {\n    return \"\", fmt.Errorf(\"upload returned status %d: %s\", resp.StatusCode, respBody)\n}\n// after\nif resp.StatusCode == http.StatusUnauthorized {\n    p.invalidateToken() // force token refresh on next call\n    return \"\", fmt.Errorf(\"upload returned status %d (token expired?): %s\", resp.StatusCode, respBody)\n}\nif resp.StatusCode != http.StatusOK {\n    return \"\", fmt.Errorf(\"upload returned status %d: %s\", resp.StatusCode, respBody)\n}","handlingStrategy":"try-catch","validationCode":"// before uploading, ensure the media type and size are valid\nvalidTypes := map[string]bool{\"image\": true, \"voice\": true, \"video\": true, \"file\": true}\nif !validTypes[mediaType] {\n    return fmt.Errorf(\"unsupported media type %q\", mediaType)\n}\nlimits := map[string]int64{\"image\": 2 << 20, \"voice\": 2 << 20, \"video\": 20 << 20, \"file\": 20 << 20}\nif int64(len(data)) > limits[mediaType] {\n    return fmt.Errorf(\"media too large for type %s\", mediaType)\n}","typeGuard":"func isAuthStatus(code int) bool {\n    return code == http.StatusUnauthorized || code == http.StatusForbidden\n}","tryCatchPattern":"mediaID, err := uploadMedia(ctx, data, mediaType)\nif err != nil {\n    var statusErr interface{ error }\n    if strings.Contains(err.Error(), \"status 401\") || strings.Contains(err.Error(), \"status 403\") {\n        // refresh token and retry once\n        p.invalidateToken()\n        mediaID, err = uploadMedia(ctx, data, mediaType)\n    }\n    if err != nil {\n        return err\n    }\n}","preventionTips":["Refresh access tokens before expiry instead of reusing stale ones.","Validate media type and size against DingTalk limits before uploading.","Keep DingTalk app credentials and permissions current in the developer console.","Watch DingTalk status pages during 5xx bursts and back off retries."],"tags":["http","upload","api","dingtalk"],"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"}