router-for-me/CLIProxyAPI · error

failed to parse response JSON

Error message

failed to parse response JSON: %w

What it means

countModels in cmd/fetch_codex_models/main.go unmarshals the fetched models payload into `{Models []json.RawMessage}`. If the body is not valid JSON (or not a JSON object), json.Unmarshal fails and the error wraps as `failed to parse response JSON: %w`. This means the endpoint answered 2xx but the body is not the expected JSON document — typically an HTML error page, empty body, or a proxy interception.

Solutions

  1. Capture the raw body (curl the same models URL with the same token) and inspect what was actually returned.
  2. Bypass or configure the intercepting proxy; ensure no HTML block page is served for chatgpt.com backend endpoints.
  3. Retry once — truncated bodies from connection cuts are transient.
  4. If the shape genuinely changed upstream, update cmd/fetch_codex_models parsing and check CLIProxyAPI updates.
Defensive patterns

Strategy: type-guard

Validate before calling

// Pre-validate that the fetched body is JSON before counting
func isJSONBody(b []byte) bool {
    var v any
    return json.Unmarshal(b, &v) == nil
}

Type guard

func isJSONParseFailure(err error) bool {
    return err != nil && strings.Contains(err.Error(), "failed to parse response JSON")
}

Try / catch

if isJSONParseFailure(err) {
    // inspect/save the raw body; likely an HTML block page from an intercepting proxy
    logBodyForDiagnosis(raw)
}

Prevention

When it happens

Trigger: 2xx response containing HTML (captive portal, corporate proxy block page, Cloudflare interstitial); empty or truncated body due to connection cut mid-transfer; wrong URL composition; response shape changed to a non-object JSON scalar.

Common situations: Running the fetch tool behind corporate proxies that inject HTML; CDN/WAF challenges; a truncated response from an unstable network; upstream A/B changes to the endpoint.

Understand the failure class

Related errors


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

Appendix: source

Thrown at cmd/fetch_codex_models/main.go:302

func codexModelsURL(clientVersion string) (string, error) {
	u, err := url.Parse(codexModelsBaseURL + codexModelsPath)
	if err != nil {
		return "", err
	}
	if strings.TrimSpace(clientVersion) != "" {
		q := u.Query()
		q.Set("client_version", strings.TrimSpace(clientVersion))
		u.RawQuery = q.Encode()
	}
	return u.String(), nil
}

func countModels(raw []byte) (int, error) {
	var payload struct {
		Models []json.RawMessage `json:"models"`
	}
	if err := json.Unmarshal(raw, &payload); err != nil {
		return 0, fmt.Errorf("failed to parse response JSON: %w", err)
	}
	// Keep this check intentionally loose: fetch_codex_models dumps the upstream
	// Codex API payload. Strict CPA catalog validation belongs in
	// cmd/validate_codex_models and registry.ValidateCodexClientModelsJSON.
	if payload.Models == nil {
		return 0, fmt.Errorf("response JSON does not contain models array")
	}
	return len(payload.Models), nil
}

func prettyJSON(raw []byte) ([]byte, error) {
	var buf bytes.Buffer
	if err := json.Indent(&buf, raw, "", "  "); err != nil {
		return nil, err
	}
	buf.WriteByte('\n')
	return buf.Bytes(), nil
}

View on GitHub (pinned to 78f0c4079e)