chenhg5/cc-connect · error
upload returned status %d: %s
Error message
upload returned status %d: %s
What it means
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.
Source
Thrown at platform/dingtalk/dingtalk.go:1382
if err != nil {
return "", fmt.Errorf("create upload request: %w", err)
}
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, err := p.httpClient.Do(req)
if err != nil {
return "", fmt.Errorf("upload request: %w", err)
}
defer func() { _ = resp.Body.Close() }()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("read upload response: %w", err)
}
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("upload returned status %d: %s", resp.StatusCode, respBody)
}
slog.Debug("dingtalk: media upload response", "status", resp.StatusCode, "body", string(respBody))
var uploadResp struct {
ErrCode int `json:"errcode"`
ErrMsg string `json:"errmsg"`
MediaID string `json:"media_id"`
Type string `json:"type"`
}
if err := json.Unmarshal(respBody, &uploadResp); err != nil {
return "", fmt.Errorf("decode upload response: %w, body: %s", err, respBody)
}
if uploadResp.ErrCode != 0 {
return "", fmt.Errorf("upload API error %d: %s", uploadResp.ErrCode, uploadResp.ErrMsg)
}
View on GitHub (pinned to 4000b2338a)
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.
Example fix
// before
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("upload returned status %d: %s", resp.StatusCode, respBody)
}
// after
if resp.StatusCode == http.StatusUnauthorized {
p.invalidateToken() // force token refresh on next call
return "", fmt.Errorf("upload returned status %d (token expired?): %s", resp.StatusCode, respBody)
}
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("upload returned status %d: %s", resp.StatusCode, respBody)
} Defensive patterns
Strategy: try-catch
Validate before calling
// before uploading, ensure the media type and size are valid
validTypes := map[string]bool{"image": true, "voice": true, "video": true, "file": true}
if !validTypes[mediaType] {
return fmt.Errorf("unsupported media type %q", mediaType)
}
limits := map[string]int64{"image": 2 << 20, "voice": 2 << 20, "video": 20 << 20, "file": 20 << 20}
if int64(len(data)) > limits[mediaType] {
return fmt.Errorf("media too large for type %s", mediaType)
} Type guard
func isAuthStatus(code int) bool {
return code == http.StatusUnauthorized || code == http.StatusForbidden
} Try / catch
mediaID, err := uploadMedia(ctx, data, mediaType)
if err != nil {
var statusErr interface{ error }
if strings.Contains(err.Error(), "status 401") || strings.Contains(err.Error(), "status 403") {
// refresh token and retry once
p.invalidateToken()
mediaID, err = uploadMedia(ctx, data, mediaType)
}
if err != nil {
return err
}
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- create AI card: status=%d, body=%s
- stream AI card: status=%d, body=%s
- create request: %w
- do request: %w
- api returned status %d
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/99b41f20cf1ccb82.
Report an issue: GitHub.