chenhg5/cc-connect · error

upload url: HTTP %d: %s

Error message

upload url: HTTP %d: %s

What it means

Returned when the MAX /uploads endpoint responds with a non-200 status during the first step of the two-step upload. The library includes the HTTP status code and up to 512 bytes of the response body so the exact server-side rejection is visible. The upload never started — no upload URL was obtained.

Source

Thrown at platform/max/max.go:558

	defer cancel()

	urlReq, err := http.NewRequestWithContext(uploadCtx, http.MethodPost, p.apiBase+"/uploads", nil)
	if err != nil {
		return "", err
	}
	p.setAuth(urlReq)
	q := urlReq.URL.Query()
	q.Set("type", kind)
	urlReq.URL.RawQuery = q.Encode()

	urlResp, err := p.uploadClient.Do(urlReq)
	if err != nil {
		return "", fmt.Errorf("request upload url: %w", err)
	}
	defer urlResp.Body.Close()
	if urlResp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(io.LimitReader(urlResp.Body, 512))
		return "", fmt.Errorf("upload url: HTTP %d: %s", urlResp.StatusCode, body)
	}
	var urlInfo struct {
		URL   string `json:"url"`
		Token string `json:"token"`
	}
	if err := json.NewDecoder(urlResp.Body).Decode(&urlInfo); err != nil {
		return "", fmt.Errorf("decode upload url: %w", err)
	}
	if urlInfo.URL == "" {
		return "", fmt.Errorf("upload url: empty url in response")
	}

	if filename == "" {
		filename = defaultFilename(kind)
	}
	var buf bytes.Buffer
	mw := multipart.NewWriter(&buf)
	fw, err := mw.CreateFormFile("data", filename)

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Read the status/body in the error message to get the exact rejection reason
  2. Verify the bot token used to build urlReq is valid and not expired
  3. Check the kind parameter matches what the API accepts for the attachment type
  4. If 413, reduce file size before uploading; if 429/5xx, retry with backoff

Example fix

// before
return "", fmt.Errorf("upload url: HTTP %d: %s", urlResp.StatusCode, body)
// after (caller-side handling)
if err != nil && strings.Contains(err.Error(), "HTTP 401") {
    return fmt.Errorf("max: upload auth failed, check bot token: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if botToken == "" { return errors.New("bot token missing; cannot upload attachment") }

Try / catch

if err != nil {
    switch {
    case strings.Contains(err.Error(), "HTTP 401"), strings.Contains(err.Error(), "HTTP 403"):
        return fmt.Errorf("check bot token: %w", err)
    case strings.Contains(err.Error(), "HTTP 429"), strings.Contains(err.Error(), "HTTP 5"):
        // retry with backoff
    default:
        return err
    }
}

Prevention

When it happens

Trigger: uploadAttachment (via SendImage/SendFile/SendAudio) receiving 401/403 (invalid bot token), 400 (unsupported type=<kind> value), 413 (declared file too large), or 5xx from the /uploads endpoint.

Common situations: Expired or wrong bot token; sending an attachment kind the API does not accept (e.g. wrong type parameter); MAX API outage; upload quota exhausted on the bot.

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