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

  1. Download the attachment promptly (CDN URLs expire); if 403, re-fetch the message to get a fresh URL
  2. Handle 404 by skipping the attachment gracefully with a user-visible note
  3. Retry 5xx with backoff
  4. 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

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


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/fd7ffcc4843d3745. Report an issue: GitHub.