chenhg5/cc-connect · error

token response decode: %w

Error message

token response decode: %w

What it means

The token endpoint returned HTTP 200 but its body could not be parsed as the expected JSON ({"access_token":..., "expires_in":...}). refreshToken wraps the json.Decoder error with this message (platform/qqbot/qqbot.go:590). Usually indicates an HTML error page, proxy interstitial, or an API response-format change.

Source

Thrown at platform/qqbot/qqbot.go:590

	})

	resp, err := core.HTTPClient.Post(tokenURL, "application/json", bytes.NewReader(body))
	if err != nil {
		return fmt.Errorf("token request failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		raw, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("token request returned %d: %s", resp.StatusCode, raw)
	}

	var result struct {
		AccessToken string `json:"access_token"`
		ExpiresIn   string `json:"expires_in"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return fmt.Errorf("token response decode: %w", err)
	}
	if result.AccessToken == "" {
		return fmt.Errorf("empty access_token in response")
	}

	var expiresSec int
	_, _ = fmt.Sscanf(result.ExpiresIn, "%d", &expiresSec)
	if expiresSec <= 0 {
		expiresSec = 7200
	}

	p.tokenMu.Lock()
	p.token = result.AccessToken
	p.tokenExpiry = time.Now().Add(time.Duration(expiresSec) * time.Second)
	p.tokenMu.Unlock()

	slog.Debug("qqbot: access token refreshed", "expires_in", expiresSec)
	return nil

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Inspect the wrapped decode error; if it mentions HTML, a proxy/portal is intercepting traffic — bypass or fix the proxy.
  2. Verify the token endpoint URL has not been changed and the deployed version of cc-connect is current.
  3. Test the endpoint directly with curl using the same appId/clientSecret payload.
  4. Update cc-connect if QQ changed the response schema.
Defensive patterns

Strategy: retry

Validate before calling

// preflight: ensure the token endpoint returns JSON
resp, err := http.Post(tokenURL, "application/json", body)
if err == nil {
    var probe map[string]any
    if json.NewDecoder(resp.Body).Decode(&probe) != nil {
        log.Println("token endpoint not returning JSON — check proxy/portal")
    }
    resp.Body.Close()
}

Try / catch

if err := platform.Start(ctx); err != nil {
    if strings.Contains(err.Error(), "token response decode") {
        slog.Error("qqbot token endpoint returned non-JSON (proxy/portal or API change)", "err", err)
        // retry once, then fall back to manual endpoint check
    }
}

Prevention

When it happens

Trigger: refreshToken receives a 200 response whose body is not valid JSON or not the expected shape: captive portal/proxy HTML page, truncated response, or QQ changing the token endpoint response format.

Common situations: Hotel/corporate captive portals intercepting HTTPS (rare with TLS), a transparent proxy returning HTML, or running an old cc-connect version against an updated QQ API.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/19d0128972a1aef6. Report an issue: GitHub.