Tencent/WeKnora · error
download failed: status=%d
Error message
download failed: status=%d
What it means
downloadFromURL treats any non-200 HTTP status from the file host as a failure and returns 'download failed: status=%d' with the actual status code, closing the response body. WeCom media URLs can expire or the media can be deleted, producing 403/404. The response body is discarded, so retry logic must rely on the status code alone.
Source
Thrown at internal/im/wecom/webhook_adapter.go:588
if !isAllowedIMAPIHost(rawURL, extraAllowedHost) {
if err := secutils.ValidateURLForSSRF(rawURL); err != nil {
return nil, "", fmt.Errorf("URL rejected for security reasons: %v", err)
}
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, rawURL, nil)
if err != nil {
return nil, "", fmt.Errorf("create request: %w", err)
}
resp, err := httpClient.Do(req)
if err != nil {
return nil, "", fmt.Errorf("download: %w", err)
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return nil, "", fmt.Errorf("download failed: status=%d", resp.StatusCode)
}
logger.Debugf(ctx, "[WeCom] Download response: status=%d content-type=%s content-disposition=%s",
resp.StatusCode, resp.Header.Get("Content-Type"), resp.Header.Get("Content-Disposition"))
// Try to extract filename from Content-Disposition header.
// Supports both standard filename and RFC 5987 filename* parameters.
if cd := resp.Header.Get("Content-Disposition"); cd != "" {
if _, params, err := mime.ParseMediaType(cd); err == nil {
// Prefer filename* (RFC 5987, already decoded by mime.ParseMediaType)
if fn := params["filename"]; fn != "" {
fileName = fn
}
} else {
// Fallback: manual extraction for malformed headers
if idx := strings.Index(cd, "filename="); idx >= 0 {
extracted := strings.Trim(cd[idx+len("filename="):], "\" ")
if extracted != "" {View on GitHub (pinned to 988cbb0330)
Solutions
- Capture the status code from the error string and retry only on 5xx/429 with backoff — 403/404 mean the media is gone
- For expired temporary media, ask the user to resend or fetch media promptly on message receipt
- Ensure the WeCom access_token used for GetMedia is fresh (refresh on 40014/42001) before blaming the URL
- Log the URL host and status to identify CDN-side rate limiting vs expiry
Example fix
// before: blind retry
for { rc, _, err := downloadFromURL(ctx, url, name, host); if err == nil { break } }
// after: retry only transient statuses
if strings.Contains(err.Error(), "status=500") || strings.Contains(err.Error(), "status=429") {
time.Sleep(backoff); continue
}
break // non-retryable Defensive patterns
Strategy: retry
Try / catch
rc, name, err := adapter.DownloadFile(ctx, msg)
if err != nil && strings.Contains(err.Error(), "download failed: status=") {
status := parseStatus(err)
if status >= 500 || status == 429 {
return retryWithBackoff(ctx) // transient
}
return err // 403/404: media expired or deleted, do not retry
} Prevention
- Download media immediately on message receipt — temporary media expires
- Refresh WeCom access_token on expiry before retrying GetMedia downloads
- Use bounded exponential backoff and only retry 5xx/429
- Monitor status-code distribution of media downloads to spot CDN issues
When it happens
Trigger: Downloading a temporary media URL after its expiry (WeCom temporary media expires ~3 days); media deleted by the user; rate limiting (429) from the media CDN; transient 5xx from WeCom servers.
Common situations: Queued jobs retrying long after the message arrived and the media URL expired; fetching MediaId through GetMedia with an expired access_token (yields 4xx); bursts of downloads hitting CDN rate limits.
Related errors
- download failed with status %d
- create download request: %w
- download file: %w
- execute request: %w
- no file key (URL or media_id) in message
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/96e6cb0ea0450a1d.
Report an issue: GitHub.