zeroclaw-labs/zeroclaw · error · anyhow::Error

OAuth refresh failed: {detail}

Error message

OAuth refresh failed: {detail}

What it means

The Qwen OAuth token endpoint answered HTTP 2xx but the JSON body carries a non-empty `error` field, so ZeroClaw surfaces error_description (or the bare error code). Token endpoints often report soft failures like invalid_grant with a 200 status, so the status check alone cannot classify the failure. The refresh aborts and provider construction fails.

Source

Thrown at crates/zeroclaw-providers/src/lib.rs:358

            ERROR,
            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
                .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                .with_attrs(::serde_json::json!({
                    "oauth_provider": "qwen",
                    "phase": "refresh_parse",
                })),
            "qwen: OAuth refresh response is not JSON"
        );
        anyhow::Error::msg("OAuth refresh response is not JSON")
    })?;

    if let Some(error_code) = payload
        .error
        .as_deref()
        .filter(|value| !value.trim().is_empty())
    {
        let detail = payload.error_description.as_deref().unwrap_or(error_code);
        anyhow::bail!("OAuth refresh failed: {detail}");
    }

    let access_token = payload
        .access_token
        .as_deref()
        .map(str::trim)
        .filter(|token| !token.is_empty())
        .ok_or_else(|| {
            ::zeroclaw_log::record!(
                ERROR,
                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
                    .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                    .with_attrs(::serde_json::json!({
                        "oauth_provider": "qwen",
                        "field": "access_token",
                    })),
                "qwen: OAuth refresh response missing access_token"
            );

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Re-login with `qwen login` to replace the refresh token
  2. Read error_description: invalid_grant means re-auth; invalid_client means a client_id mismatch — upgrade ZeroClaw
  3. Delete ~/.qwen/oauth_creds.json if it is corrupted, then log in again
Defensive patterns

Strategy: try-catch

Try / catch

match refresh_qwen_oauth_access_token(&token, &client_id) {
    Err(e) => {
        let msg = e.to_string();
        if msg.contains("invalid_grant") || msg.contains("expired") {
            return Reauth::required("qwen login"); // refresh token is dead
        }
        if msg.contains("invalid_client") { return Upgrade::required(); }
        Err(e)
    }
    ok => ok,
}

Prevention

When it happens

Trigger: Refresh response body such as `{"error":"invalid_grant","error_description":"refresh token expired"}` with HTTP 200; invalid_client or bad client_id delivered the same way.

Common situations: Refresh token revoked after logout or password change on the Qwen side; ZeroClaw version drift changing the expected client_id; partially corrupted oauth_creds.json with a malformed refresh_token.

Related errors


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