router-for-me/CLIProxyAPI · error

decode response

Error message

decode response: %w

What it means

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.

Solutions

  1. Log or inspect bodyBytes (already captured in the caller) to see what non-JSON payload came back — an HTML title usually names the interceptor
  2. Bypass or authenticate with the captive portal / corporate proxy, then retry
  3. If testing, make the stub return Content-Type: application/json with a valid loadCodeAssist-shaped object
Defensive patterns

Strategy: try-catch

Try / catch

if _, err := auth.FetchProjectID(ctx, token); err != nil {
    var syn *json.SyntaxError
    if errors.As(err, &syn) {
        log.Errorf("non-JSON 2xx from loadCodeAssist — likely captive portal/proxy rewrite: %v", err)
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/e1008682205ee5af. Report an issue: GitHub.

Appendix: source

Thrown at internal/auth/antigravity/auth.go:269

	}
	defer func() {
		if errClose := resp.Body.Close(); errClose != nil {
			log.Errorf("antigravity loadCodeAssist: close body error: %v", errClose)
		}
	}()

	bodyBytes, errRead := io.ReadAll(resp.Body)
	if errRead != nil {
		return "", fmt.Errorf("read response: %w", errRead)
	}

	if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
		return "", fmt.Errorf("request failed with status %d: %s", resp.StatusCode, strings.TrimSpace(string(bodyBytes)))
	}

	var loadResp map[string]any
	if errDecode := json.Unmarshal(bodyBytes, &loadResp); errDecode != nil {
		return "", fmt.Errorf("decode response: %w", errDecode)
	}

	projectID := extractCloudaicompanionProject(loadResp)

	if projectID == "" {
		projectID, err = o.OnboardUser(ctx, accessToken, defaultAntigravityTierID(loadResp))
		if err != nil {
			return "", err
		}
		if projectID == "" {
			return "", fmt.Errorf("project id not found in loadCodeAssist or onboardUser response")
		}
		return projectID, nil
	}

	return projectID, nil
}

View on GitHub (pinned to 78f0c4079e)