{"record":{"id":"e1008682205ee5af","repo":"router-for-me/CLIProxyAPI","slug":"decode-response-w","errorCode":null,"errorMessage":"decode response: %w","messagePattern":"decode response: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/antigravity/auth.go","lineNumber":269,"sourceCode":"\t}\n\tdefer func() {\n\t\tif errClose := resp.Body.Close(); errClose != nil {\n\t\t\tlog.Errorf(\"antigravity loadCodeAssist: close body error: %v\", errClose)\n\t\t}\n\t}()\n\n\tbodyBytes, errRead := io.ReadAll(resp.Body)\n\tif errRead != nil {\n\t\treturn \"\", fmt.Errorf(\"read response: %w\", errRead)\n\t}\n\n\tif resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {\n\t\treturn \"\", fmt.Errorf(\"request failed with status %d: %s\", resp.StatusCode, strings.TrimSpace(string(bodyBytes)))\n\t}\n\n\tvar loadResp map[string]any\n\tif errDecode := json.Unmarshal(bodyBytes, &loadResp); errDecode != nil {\n\t\treturn \"\", fmt.Errorf(\"decode response: %w\", errDecode)\n\t}\n\n\tprojectID := extractCloudaicompanionProject(loadResp)\n\n\tif projectID == \"\" {\n\t\tprojectID, err = o.OnboardUser(ctx, accessToken, defaultAntigravityTierID(loadResp))\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif projectID == \"\" {\n\t\t\treturn \"\", fmt.Errorf(\"project id not found in loadCodeAssist or onboardUser response\")\n\t\t}\n\t\treturn projectID, nil\n\t}\n\n\treturn projectID, nil\n}\n","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/antigravity/auth.go#L251-L287","documentation":"Returned by AntigravityAuth.FetchProjectID when the loadCodeAssist endpoint returns 2xx but the body is not valid JSON. The code unconditionally json.Unmarshals the body into map[string]any, so any HTML error page, empty body, or plain-text response from an interceptor triggers this. With the real Google endpoint a 2xx always carries JSON, so this almost always means something between the client and Google (captive portal, authenticating proxy) rewrote the response.","triggerScenarios":"A captive portal or transparent proxy answering 200 with an HTML login page; a mock/stub server in tests returning 200 with an empty or non-JSON body; a misconfigured SSL-offloading appliance injecting plain text.","commonSituations":"Running the proxy on guest Wi-Fi with a captive portal; corporate TLS-inspecting middleboxes; local development against a fake cloudcode-pa endpoint that forgot to set a JSON body.","solutions":["Log or inspect bodyBytes (already captured in the caller) to see what non-JSON payload came back — an HTML title usually names the interceptor","Bypass or authenticate with the captive portal / corporate proxy, then retry","If testing, make the stub return Content-Type: application/json with a valid loadCodeAssist-shaped object"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if _, err := auth.FetchProjectID(ctx, token); err != nil {\n    var syn *json.SyntaxError\n    if errors.As(err, &syn) {\n        log.Errorf(\"non-JSON 2xx from loadCodeAssist — likely captive portal/proxy rewrite: %v\", err)\n    }\n}","preventionTips":["Complete captive-portal authentication before running the login flow","Whitelist cloudcode-pa.googleapis.com for direct, unmodified egress in proxy config"],"tags":["antigravity","json","proxy","captive-portal"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}