{"record":{"id":"64fed971ea506f32","repo":"zeroclaw-labs/zeroclaw","slug":"oauth-refresh-failed-detail","errorCode":null,"errorMessage":"OAuth refresh failed: {detail}","messagePattern":"OAuth refresh failed: (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-providers/src/lib.rs","lineNumber":358,"sourceCode":"            ERROR,\n            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)\n                .with_outcome(::zeroclaw_log::EventOutcome::Failure)\n                .with_attrs(::serde_json::json!({\n                    \"oauth_provider\": \"qwen\",\n                    \"phase\": \"refresh_parse\",\n                })),\n            \"qwen: OAuth refresh response is not JSON\"\n        );\n        anyhow::Error::msg(\"OAuth refresh response is not JSON\")\n    })?;\n\n    if let Some(error_code) = payload\n        .error\n        .as_deref()\n        .filter(|value| !value.trim().is_empty())\n    {\n        let detail = payload.error_description.as_deref().unwrap_or(error_code);\n        anyhow::bail!(\"OAuth refresh failed: {detail}\");\n    }\n\n    let access_token = payload\n        .access_token\n        .as_deref()\n        .map(str::trim)\n        .filter(|token| !token.is_empty())\n        .ok_or_else(|| {\n            ::zeroclaw_log::record!(\n                ERROR,\n                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)\n                    .with_outcome(::zeroclaw_log::EventOutcome::Failure)\n                    .with_attrs(::serde_json::json!({\n                        \"oauth_provider\": \"qwen\",\n                        \"field\": \"access_token\",\n                    })),\n                \"qwen: OAuth refresh response missing access_token\"\n            );","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-providers/src/lib.rs#L340-L376","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-login with `qwen login` to replace the refresh token","Read error_description: invalid_grant means re-auth; invalid_client means a client_id mismatch — upgrade ZeroClaw","Delete ~/.qwen/oauth_creds.json if it is corrupted, then log in again"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match refresh_qwen_oauth_access_token(&token, &client_id) {\n    Err(e) => {\n        let msg = e.to_string();\n        if msg.contains(\"invalid_grant\") || msg.contains(\"expired\") {\n            return Reauth::required(\"qwen login\"); // refresh token is dead\n        }\n        if msg.contains(\"invalid_client\") { return Upgrade::required(); }\n        Err(e)\n    }\n    ok => ok,\n}","preventionTips":["Expect soft OAuth failures with HTTP 200 — always inspect error/error_description, not just status","Re-login as soon as invalid_grant appears; repeated refresh attempts can lock the token","Delete corrupted oauth_creds.json rather than looping on refresh"],"tags":["qwen","oauth","invalid-grant","authentication"],"backgroundTag":"oauth-refresh-token-failed","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}