{"record":{"id":"dd94b37885f1a2fd","repo":"chenhg5/cc-connect","slug":"decode-upload-url-w","errorCode":null,"errorMessage":"decode upload url: %w","messagePattern":"decode upload url: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/max/max.go","lineNumber":565,"sourceCode":"\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)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif _, err := fw.Write(data); err != nil {\n\t\treturn \"\", err\n\t}\n\tif err := mw.Close(); err != nil {","sourceCodeStart":547,"sourceCodeEnd":583,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/max/max.go#L547-L583","documentation":"This wraps a JSON decode failure of the /uploads response body. The server returned HTTP 200 but the body could not be parsed into the expected {url, token} structure, indicating the API response is malformed or an unexpected shape (e.g. an HTML error page served behind a proxy with a 200 status, or an API version change).","triggerScenarios":"uploadAttachment decoding urlResp when the body is not valid JSON or lacks the expected fields — proxy/error interstitial pages with status 200, truncated responses, API contract changes, or wrong content-type responses.","commonSituations":"Reverse proxies or captive portals returning HTML with 200; MAX API version upgrade renaming response fields; decompression issues (missing Accept-Encoding handling) producing garbage bytes.","solutions":["Log the raw response body (limited) alongside the decode error to see what was actually returned","Confirm the bot-api endpoint and version — response schema may have changed","Check for proxies/gateways injecting HTML error pages with 200 status","Retry once on transient truncation; report a bug if the API response shape is confirmed correct"],"exampleFix":"// before\nif err := json.NewDecoder(urlResp.Body).Decode(&urlInfo); err != nil {\n    return \"\", fmt.Errorf(\"decode upload url: %w\", err)\n}\n// after (caller diagnostics)\nif err != nil {\n    slog.Error(\"max: bad /uploads response\", \"err\", err, \"ct\", urlResp.Header.Get(\"Content-Type\"))\n    return \"\", err\n}","handlingStrategy":"try-catch","validationCode":"ct := resp.Header.Get(\"Content-Type\"); if !strings.Contains(ct, \"application/json\") { return fmt.Errorf(\"unexpected content type: %s\", ct) }","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"decode upload url\") {\n    slog.Error(\"max: non-JSON /uploads response; check proxy and API version\", \"err\", err)\n    return fmt.Errorf(\"max upload unavailable: %w\", err)\n}","preventionTips":["Log Content-Type and a body snippet when decode fails","Pin/track the MAX Bot API version you developed against","Ensure proxies in front of the API return proper status codes, not 200+HTML","Add a canary test that hits /uploads in CI to catch contract changes early"],"tags":["json","api","upload","unexpected-response"],"backgroundTag":"json-unmarshal-failed","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"}