chenhg5/cc-connect · error
%s: CDN upload client error %d: %s
Error message
%s: CDN upload client error %d: %s
What it means
uploadBufferToCDN rejects the upload when the WeChat CDN returns an HTTP 4xx client error. The CDN signals the reason via the x-error-message header (falling back to resp.Status). Client errors are NOT retried because retrying a malformed/rejected request cannot succeed.
Source
Thrown at platform/weixin/cdn.go:203
req, err := http.NewRequestWithContext(ctx, http.MethodPost, u, bytes.NewReader(ciphertext))
if err != nil {
return "", fmt.Errorf("%s: new request: %w", label, err)
}
req.Header.Set("Content-Type", "application/octet-stream")
resp, err := client.Do(req)
if err != nil {
lastErr = err
slog.Warn("weixin: CDN upload request failed", "label", label, "attempt", attempt, "error", err)
continue
}
_, _ = io.Copy(io.Discard, io.LimitReader(resp.Body, 4096))
_ = resp.Body.Close()
if resp.StatusCode >= 400 && resp.StatusCode < 500 {
msg := resp.Header.Get("x-error-message")
if msg == "" {
msg = resp.Status
}
return "", fmt.Errorf("%s: CDN upload client error %d: %s", label, resp.StatusCode, msg)
}
if resp.StatusCode != http.StatusOK {
msg := resp.Header.Get("x-error-message")
if msg == "" {
msg = fmt.Sprintf("status %d", resp.StatusCode)
}
lastErr = fmt.Errorf("%s: CDN upload server error: %s", label, msg)
slog.Warn("weixin: CDN upload server error", "label", label, "attempt", attempt, "error", lastErr)
continue
}
dl := resp.Header.Get("x-encrypted-param")
if dl == "" {
lastErr = fmt.Errorf("%s: CDN response missing x-encrypted-param", label)
slog.Warn("weixin: CDN upload bad response", "label", label, "attempt", attempt)
continue
}
return dl, nil
}View on GitHub (pinned to 4000b2338a)
Solutions
- Inspect the %s (msg) portion of the error for the CDN's x-error-message to identify the rejection reason
- Call getUploadUrl again to get a fresh upload_param/upload_full_url and retry with the new URL
- Verify cdn_base (default https://novac2c.cdn.weixin.qq.com/c2c) is correct in config.toml
- Check that the aes_key passed to uploadBufferToCDN is exactly 16 bytes and that the filekey matches the getUploadUrl response
Example fix
// before: blind retry on any error
for i := 0; i < 3; i++ { _, err := uploadToWeixinCDN(ctx, ...); if err == nil { break } }
// after: refresh the upload URL only on client-error and retry once
if err != nil && strings.Contains(err.Error(), "CDN upload client error") {
resp, err2 := client.getUploadURL(ctx, freshGetUploadURLRequest)
if err2 == nil { /* rebuild URL and retry once */ }
} Defensive patterns
Strategy: validation
Validate before calling
// before uploading, ensure the upload URL and key are fresh and well-formed
if uploadParam == "" || len(aesKey) != 16 || time.Since(uploadURLIssuedAt) > 5*time.Minute {
// re-call getUploadUrl and re-derive aesKey
} Prevention
- Always fetch a fresh upload URL from getUploadUrl immediately before each upload
- Never cache upload_param/upload_full_url across sessions or long periods
- Validate aes_key is 16 raw bytes before uploading
- Log the x-error-message from failures to spot contract drift early
When it happens
Trigger: POST of AES-ECB ciphertext to the CDN upload URL returns status 400-499; typical causes are an expired/invalid encrypted_query_param or filekey from getUploadUrl, a wrong URL, or the CDN rejecting content metadata.
Common situations: Stale upload URL reused after expiry; filekey mismatch between getUploadUrl and the upload request; CDN base URL misconfigured in config.toml; CDN-side rejecting oversize or malformed payloads with 4xx.
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
- %s: CDN upload server error: %s
- %s: CDN response missing x-encrypted-param
- %s: CDN upload failed after %d attempts: %w
- %s: CDN upload failed after %d attempts
- http %d: %s
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/bdc9aa3624b6c6ac.
Report an issue: GitHub.