chenhg5/cc-connect · error

cdn upload: HTTP %d: %s

Error message

cdn upload: HTTP %d: %s

What it means

The MAX CDN returned a non-200 HTTP status to the multipart upload POST. The platform reads up to 512 bytes of the response body and reports the status code plus a truncated body so the developer can see the CDN's own error message. This means the upload request reached the CDN and was rejected (auth, payload, or URL problems), not a transport failure.

Source

Thrown at platform/max/max.go:601

	if err := mw.Close(); err != nil {
		return "", err
	}

	cdnReq, err := http.NewRequestWithContext(uploadCtx, http.MethodPost, urlInfo.URL, &buf)
	if err != nil {
		return "", err
	}
	p.setAuth(cdnReq)
	cdnReq.Header.Set("Content-Type", mw.FormDataContentType())

	cdnResp, err := p.uploadClient.Do(cdnReq)
	if err != nil {
		return "", fmt.Errorf("cdn upload: %w", err)
	}
	defer cdnResp.Body.Close()
	if cdnResp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(io.LimitReader(cdnResp.Body, 512))
		return "", fmt.Errorf("cdn upload: HTTP %d: %s", cdnResp.StatusCode, body)
	}
	cdnBody, err := io.ReadAll(io.LimitReader(cdnResp.Body, 64*1024))
	if err != nil {
		return "", fmt.Errorf("read cdn response: %w", err)
	}
	// MAX CDN uses different response shapes per attachment kind:
	//   image: {"photos": {"<photo_id>": {"token": "..."}}}
	//   file:  {"token": "..."}
	//   video/audio: "<retval>1</retval>" (XML) — the real token is already in urlInfo.Token
	if token := extractCDNToken(kind, cdnBody); token != "" {
		return token, nil
	}
	if urlInfo.Token != "" {
		return urlInfo.Token, nil
	}
	return "", fmt.Errorf("cdn upload: no token in response: %s", cdnBody)
}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Read the truncated body in the error — it contains the CDN's specific reason (auth vs. size vs. bad request).
  2. Minimize the delay between the /uploads call and the CDN POST; regenerate the upload URL if more than a few minutes elapsed.
  3. Verify the bot token is still valid and has upload permissions; re-test step 1 alone with curl.
  4. Check the file size against MAX's attachment limits per kind and compress or truncate as needed.
  5. Ensure the multipart field name is exactly "data" and Content-Type comes from mw.FormDataContentType() — deviations cause 400s.

Example fix

// before
urlInfo, err := getUploadURL(...)
// ... long processing ...
token, err := p.uploadAttachment(ctx, kind, data, filename) // URL may be stale
// after: fetch upload URL and POST back-to-back
urlInfo, err := requestUploadURL(ctx, kind)
if err != nil {
	return fmt.Errorf("max: get upload url: %w", err)
}
token, err := p.uploadAttachment(ctx, kind, data, filename)
if err != nil {
	return fmt.Errorf("max: upload %s: %w", kind, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check size against a conservative MAX attachment cap before uploading
const maxAttachmentBytes = 30 << 20
if len(data) > maxAttachmentBytes {
	return fmt.Errorf("attachment %s too large: %d > %d bytes", filename, len(data), maxAttachmentBytes)
}

Try / catch

token, err := p.uploadAttachment(ctx, kind, data, filename)
if err != nil {
	var statusErr interface{ HTTPStatus() int }
	if strings.Contains(err.Error(), "cdn upload: HTTP ") {
		// parse status; regenerate upload URL and retry once on 4xx
		if newURL, uerr := requestUploadURL(ctx, kind); uerr == nil {
			_ = newURL
		}
	}
}

Prevention

When it happens

Trigger: SendImage/SendFile/SendAudio where the CDN answers e.g. 401/403 (setAuth token invalid or expired), 400 (malformed multipart, wrong Content-Type, missing "data" field), 404 (the presigned URL expired between step 1 and step 2), or 413 (payload too large).

Common situations: Delaying too long between obtaining the upload URL and POSTing the file (presigned URL expiry); uploading files exceeding the CDN's per-kind size cap; revoked bot tokens; sending a kind mismatched with the URL (e.g. image bytes to a file-type URL).

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/0265ba24cb01ff03. Report an issue: GitHub.