chenhg5/cc-connect · error

cdn upload: %w

Error message

cdn upload: %w

What it means

This is the transport-level failure branch of the second step of MAX's two-step attachment upload: the multipart/form-data POST of the file bytes to the presigned CDN URL. When p.uploadClient.Do returns a non-nil error (connection failure, TLS error, DNS failure, timeout, context cancellation), the error is wrapped with the "cdn upload: " prefix and returned. It is a network/client-side failure, not an HTTP status error.

Source

Thrown at platform/max/max.go:596

		return "", err
	}
	if _, err := fw.Write(data); err != nil {
		return "", err
	}
	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 != "" {

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Check network reachability of the CDN URL host returned by /uploads (curl -v to it) — proxies and firewalls commonly block it.
  2. Reduce attachment size or increase attachmentUploadTO if large files routinely time out on your network.
  3. Confirm the caller's context isn't already near expiry; the upload inherits the session context deadline.
  4. Retry the upload with backoff — CDN transport failures are often transient.
  5. If behind a corporate proxy, configure the uploadClient's Proxy/Transport to route through it.

Example fix

// before
err := p.uploadAttachment(ctx, "image", data, "photo.png")
// after (pre-flight size check + bounded retry)
const maxUpload = 50 << 20 // 50 MiB
if len(data) > maxUpload {
	return fmt.Errorf("attachment too large: %d bytes", len(data))
}
var token string
var lastErr error
for attempt := 0; attempt < 3; attempt++ {
	token, lastErr = p.uploadAttachment(ctx, "image", data, "photo.png")
	if lastErr == nil {
		break
	}
	var netErr net.Error
	if errors.As(lastErr, &netErr) && netErr.Timeout() {
		time.Sleep(time.Duration(attempt+1) * time.Second)
		continue
	}
	break
}
Defensive patterns

Strategy: retry

Validate before calling

// Reachability pre-check before uploading
func cdnReachable(uploadClient *http.Client, url string) bool {
	u, err := neturl.Parse(url)
	if err != nil || u.Host == "" { return false }
	conn, err := net.DialTimeout("tcp", u.Host, 3*time.Second)
	if err != nil { return false }
	conn.Close()
	return true
}

Type guard

var _ net.Error
func isTimeoutOrCancel(err error) bool {
	return errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) || (func() bool { var n net.Error; return errors.As(err, &n) && n.Timeout() })()
}

Try / catch

token, err := p.uploadAttachment(ctx, kind, data, filename)
if err != nil {
	var nerr net.Error
	if errors.As(err, &nerr) && nerr.Timeout() {
		// bounded retry with backoff for transient transport failures
	}
	if errors.Is(err, context.Canceled) {
		return fmt.Errorf("max: upload canceled: %w", err)
	}
}

Prevention

When it happens

Trigger: Calling SendImage/SendFile/SendAudio when the CDN endpoint is unreachable, the 5-minute attachmentUploadTO deadline or caller context expires mid-upload, TLS handshake fails, DNS resolution fails, or the connection is reset while streaming the multipart body for large files.

Common situations: Uploading large files over slow links hitting the 5-minute timeout; firewall/proxy blocking the CDN host returned by /uploads; transient CDN outages; IPv6 connectivity issues; mobile/offline networks; canceling the enclosing session context.

Related errors


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