zeroclaw-labs/zeroclaw · error

QQ token request failed ({status}): {err}

Error message

QQ token request failed ({status}): {err}

What it means

fetch_access_token POSTs {"appId", "clientSecret"} to https://bots.qq.com/app/getAppAccessToken; any non-2xx response bails with the HTTP status and response body. The caller fetch_access_token_with_retry already retries with jittered exponential backoff up to AUTH_RETRY_MAX_ATTEMPTS, so this error escaping means every attempt failed — most commonly invalid credentials or a blocked network path.

Source

Thrown at crates/zeroclaw-channels/src/qq.rs:430

    /// Fetch an access token from QQ's OAuth2 endpoint.
    async fn fetch_access_token(&self) -> anyhow::Result<(String, u64)> {
        let body = json!({
            "appId": self.app_id,
            "clientSecret": self.app_secret,
        });

        let resp = self
            .http_client()
            .post(QQ_AUTH_URL)
            .json(&body)
            .send()
            .await?;

        if !resp.status().is_success() {
            let status = resp.status();
            let err = resp.text().await.unwrap_or_default();
            anyhow::bail!("QQ token request failed ({status}): {err}");
        }

        let data: serde_json::Value = resp.json().await?;
        let token = data
            .get("access_token")
            .and_then(|t| t.as_str())
            .ok_or_else(|| {
                ::zeroclaw_log::record!(
                    WARN,
                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Reject)
                        .with_outcome(::zeroclaw_log::EventOutcome::Failure),
                    "Missing access_token in QQ response"
                );
                anyhow::Error::msg("Missing access_token in QQ response")
            })?
            .to_string();

        let expires_in = data

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Read the status and body in the message: 4xx means credentials/app status — fix app_id/app_secret in [channels.qq.<alias>]; 5xx means QQ-side, retry later
  2. Verify outbound connectivity to https://bots.qq.com (curl) and check any channel.qq proxy_url setting
  3. After fixing credentials, let the built-in retry handle the rest; no code change needed for transient failures

Example fix

# before
[channels.qq.main]
app_id = "102030405"
app_secret = "stale-secret"        # -> QQ token request failed (401 Unauthorized)

# after
[channels.qq.main]
app_id = "102030405"
app_secret = "current-secret-from-qq-open-platform"
Defensive patterns

Strategy: retry

Validate before calling

// Fail fast on missing/obviously-wrong credentials before the first token call:
fn qq_credentials_present(cfg: &Config, alias: &str) -> anyhow::Result<()> {
    let qq = cfg.channels.qq.get(alias).context("[channels.qq.{alias}] not configured")?;
    anyhow::ensure!(!qq.app_id.trim().is_empty(), "qq app_id is empty");
    anyhow::ensure!(!qq.app_secret.trim().is_empty(), "qq app_secret is empty");
    Ok(())
}

Try / catch

// The library already retries with jittered backoff; only handle terminal failure:
match qq.listen().await {
    Err(e) if e.to_string().contains("QQ token request failed") => {
        let body = e.to_string();
        if body.contains("401") || body.contains("400") {
            // Credential problem: stop retrying, alert operator to fix app_id/app_secret.
        } else {
            // 5xx/platform issue: restart the channel later (external backoff).
        }
    }
    rest => rest?,
}

Prevention

When it happens

Trigger: Wrong app_id or app_secret in [channels.qq.<alias>]; the QQ open platform rejecting/ suspending the app (body usually says so); egress firewall or a broken channel.qq proxy_url breaking the POST to bots.qq.com.

Common situations: Rotated QQ app secrets not updated in config; sandbox vs production app credentials mixed up; environments without direct outbound access to bots.qq.com; copying app_id from the QQ console with whitespace/quotes.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/7408ba0d0dda817b. Report an issue: GitHub.