chenhg5/cc-connect · error

qqbot: get token: %w

Error message

qqbot: get token: %w

What it means

This error is raised inside apiRequestJSON when p.getAccessToken() fails before any HTTP request is made. getAccessToken fetches (and caches) the app-wide access token using the configured AppID/AppSecret; failure means credentials are wrong, the token endpoint is unreachable, or the cached token could not be refreshed. All qqbot API calls (sendMessage, uploadRichMedia, ackInteraction) funnel through here, so this blocks every outbound API interaction.

Source

Thrown at platform/qqbot/qqbot.go:316

		return "", fmt.Errorf("qqbot: upload rich media: empty file_info")
	}
	return result.FileInfo, nil
}

// apiRequestJSON is like apiRequest but also decodes the response body into result.
func (p *Platform) apiRequestJSON(method, url string, body any, result any) error {
	var bodyReader io.Reader
	if body != nil {
		data, err := json.Marshal(body)
		if err != nil {
			return fmt.Errorf("qqbot: marshal body: %w", err)
		}
		bodyReader = bytes.NewReader(data)
	}

	token, err := p.getAccessToken()
	if err != nil {
		return fmt.Errorf("qqbot: get token: %w", err)
	}

	req, err := http.NewRequest(method, url, bodyReader)
	if err != nil {
		return err
	}
	req.Header.Set("Authorization", "QQBot "+token)
	req.Header.Set("Content-Type", "application/json")

	resp, err := core.HTTPClient.Do(req)
	if err != nil {
		return fmt.Errorf("qqbot: api request failed: %w", err)
	}
	defer resp.Body.Close()

	// Retry once on 401
	if resp.StatusCode == http.StatusUnauthorized {
		if err := p.refreshToken(); err != nil {

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Verify appId and appSecret in the [platform.qqbot] config section against the QQ Open Platform console.
  2. Test network reachability to the QQ API token endpoint from the host running cc-connect (curl the apiBase).
  3. Run `cc-connect doctor` to check credentials and connectivity.
  4. Check that the qqbot app is approved/enabled and the secret was not recently rotated.
  5. Inspect the wrapped error text — it names the underlying cause (auth refused vs network timeout).

Example fix

// before (config.toml)
[platforms.qqbot]
appId = ""
appSecret = "stale-secret"
// after
[platforms.qqbot]
appId = "123456789"
appSecret = "current-secret-from-console"
Defensive patterns

Strategy: validation

Validate before calling

if cfg.QQBot.AppID == "" || cfg.QQBot.AppSecret == "" {
    return fmt.Errorf("qqbot: appId/appSecret must be set in config")
}
if err := doctorCheckEndpoint(qqbotTokenURL); err != nil {
    return fmt.Errorf("qqbot: token endpoint unreachable: %w", err)
}

Try / catch

if err := p.SendFile(ctx, rctx, f); err != nil {
    if strings.Contains(err.Error(), "get token") {
        // credentials/network problem; do not retry blindly, alert + verify config
        slog.Error("qqbot auth unavailable", "err", err)
    }
}

Prevention

When it happens

Trigger: Any of uploadRichMedia, ackInteraction, or sendMessage calls apiRequestJSON and getAccessToken returns an error: invalid AppID/AppSecret in config.toml, token endpoint unreachable, or token exchange rejected (e.g. wrong secret, suspended app).

Common situations: Misconfigured qqbot appId/appSecret in config.toml; credentials rotated in the QQ Open Platform console but not updated locally; network egress blocked to the QQ API host; app disabled or sandbox/scope restrictions.

Related errors


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