{"record":{"id":"a5f565f5d21927a9","repo":"chenhg5/cc-connect","slug":"upload-url-http-d-s","errorCode":null,"errorMessage":"upload url: HTTP %d: %s","messagePattern":"upload url: HTTP (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/max/max.go","lineNumber":558,"sourceCode":"\tdefer cancel()\n\n\turlReq, err := http.NewRequestWithContext(uploadCtx, http.MethodPost, p.apiBase+\"/uploads\", nil)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tp.setAuth(urlReq)\n\tq := urlReq.URL.Query()\n\tq.Set(\"type\", kind)\n\turlReq.URL.RawQuery = q.Encode()\n\n\turlResp, err := p.uploadClient.Do(urlReq)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"request upload url: %w\", err)\n\t}\n\tdefer urlResp.Body.Close()\n\tif urlResp.StatusCode != http.StatusOK {\n\t\tbody, _ := io.ReadAll(io.LimitReader(urlResp.Body, 512))\n\t\treturn \"\", fmt.Errorf(\"upload url: HTTP %d: %s\", urlResp.StatusCode, body)\n\t}\n\tvar urlInfo struct {\n\t\tURL   string `json:\"url\"`\n\t\tToken string `json:\"token\"`\n\t}\n\tif err := json.NewDecoder(urlResp.Body).Decode(&urlInfo); err != nil {\n\t\treturn \"\", fmt.Errorf(\"decode upload url: %w\", err)\n\t}\n\tif urlInfo.URL == \"\" {\n\t\treturn \"\", fmt.Errorf(\"upload url: empty url in response\")\n\t}\n\n\tif filename == \"\" {\n\t\tfilename = defaultFilename(kind)\n\t}\n\tvar buf bytes.Buffer\n\tmw := multipart.NewWriter(&buf)\n\tfw, err := mw.CreateFormFile(\"data\", filename)","sourceCodeStart":540,"sourceCodeEnd":576,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/max/max.go#L540-L576","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read the status/body in the error message to get the exact rejection reason","Verify the bot token used to build urlReq is valid and not expired","Check the kind parameter matches what the API accepts for the attachment type","If 413, reduce file size before uploading; if 429/5xx, retry with backoff"],"exampleFix":"// before\nreturn \"\", fmt.Errorf(\"upload url: HTTP %d: %s\", urlResp.StatusCode, body)\n// after (caller-side handling)\nif err != nil && strings.Contains(err.Error(), \"HTTP 401\") {\n    return fmt.Errorf(\"max: upload auth failed, check bot token: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"if botToken == \"\" { return errors.New(\"bot token missing; cannot upload attachment\") }","typeGuard":null,"tryCatchPattern":"if err != nil {\n    switch {\n    case strings.Contains(err.Error(), \"HTTP 401\"), strings.Contains(err.Error(), \"HTTP 403\"):\n        return fmt.Errorf(\"check bot token: %w\", err)\n    case strings.Contains(err.Error(), \"HTTP 429\"), strings.Contains(err.Error(), \"HTTP 5\"):\n        // retry with backoff\n    default:\n        return err\n    }\n}","preventionTips":["Validate the bot token at startup (TestConnection/doctor flow)","Keep declared Content-Type/kind consistent with the actual attachment","Handle 429 with exponential backoff to respect rate limits","Monitor the body text in the error for API quota/auth messages"],"tags":["http","api","upload","max"],"backgroundTag":"http-error-response","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}