chenhg5/cc-connect · error
download %s: status %d
Error message
download %s: status %d
What it means
download %s: status %d is thrown when the platform's HTTP file download helper receives a non-200 status code. It fetches attachments/images referenced in messages (e.g. replied-to message attachments) with a size limit. The URL and status code are included so the failing resource is identifiable.
Source
Thrown at platform/discord/discord.go:1527
slog.Error("discord: download file failed", "url", att.URL, "file_name", att.Filename, "error", err)
continue
}
files = append(files, core.FileAttachment{MimeType: att.ContentType, Data: data, FileName: att.Filename})
}
}
return images, files, audio
}
const maxDownloadBytes = 50 << 20 // 50 MiB
func downloadURL(u string) ([]byte, error) {
resp, err := core.HTTPClient.Get(u)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("download %s: status %d", u, resp.StatusCode)
}
return io.ReadAll(io.LimitReader(resp.Body, maxDownloadBytes+1))
}
// applyReferencedMessage prepends the replied-to message's author and content
// to content and prepends any images from the referenced message's attachments.
// Image attachments (width > 0) are downloaded via download and prepended so the
// agent sees them before the current message's own images.
func applyReferencedMessage(ref *discordgo.Message, content string, images []core.ImageAttachment, download func(string) ([]byte, error)) (string, []core.ImageAttachment) {
author := ""
if ref.Author != nil {
author = ref.Author.Username
}
content = "[replying to " + author + ": " + ref.Content + "]\n" + content
for _, att := range ref.Attachments {
if att.Width > 0 && att.Height > 0 {
data, err := download(att.URL)
if err != nil {View on GitHub (pinned to 4000b2338a)
Solutions
- Download the attachment promptly (CDN URLs expire); if 403, re-fetch the message to get a fresh URL
- Handle 404 by skipping the attachment gracefully with a user-visible note
- Retry 5xx with backoff
- Verify proxy/network configuration if all downloads fail
Example fix
// before
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("download %s: status %d", u, resp.StatusCode)
}
// after
if resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusNotFound {
return nil, core.ErrAttachmentUnavailable // caller skips gracefully
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("download %s: status %d", u, resp.StatusCode)
} Defensive patterns
Strategy: fallback
Validate before calling
if u == "" { return errors.New("empty download URL") } Try / catch
data, err := downloadAttachment(u)
if err != nil {
log.Warn("attachment unavailable", "url", u, "err", err)
return nil, nil // skip attachment gracefully
} Prevention
- Download attachments immediately upon message receipt (CDN URLs expire)
- Cap sizes with io.LimitReader as the helper already does
- Retry transient 5xx; treat 403/404 as permanent skip
When it happens
Trigger: core.HTTPClient.Get succeeded but resp.StatusCode != 200 when downloading an attachment URL (discord.go:1527) — typically 403 (expired CDN signature), 404 (deleted attachment), or 5xx.
Common situations: Discord CDN attachment URLs expiring after a short TTL; user deleted the attachment before the bot fetched it; network proxy returning an error page; oversized file capped downstream by maxDownloadBytes.
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
- range chunk retries exhausted
- HTTP %d
- download exceeds --max-bytes: %d > %d
- qwen tts: download audio: %w
- download failed from all sources: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/fd7ffcc4843d3745.
Report an issue: GitHub.