{"record":{"id":"f66d32a144e6d142","repo":"sigoden/aichat","slug":"failed-to-parse-expires-in-of-access-token","errorCode":null,"errorMessage":"Failed to parse expires_in of access_token","messagePattern":"Failed to parse expires_in of access_token","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/client/vertexai.rs","lineNumber":461,"sourceCode":"            Ok(ModelCategory::Mistral)\n        } else {\n            unsupported_model!(s)\n        }\n    }\n}\n\npub async fn prepare_gcloud_access_token(\n    client: &reqwest::Client,\n    client_name: &str,\n    adc_file: &Option<String>,\n) -> Result<()> {\n    if !is_valid_access_token(client_name) {\n        let (token, expires_in) = fetch_access_token(client, adc_file)\n            .await\n            .with_context(|| \"Failed to fetch access token\")?;\n        let expires_at = Utc::now()\n            + Duration::try_seconds(expires_in)\n                .ok_or_else(|| anyhow!(\"Failed to parse expires_in of access_token\"))?;\n        set_access_token(client_name, token, expires_at.timestamp())\n    }\n    Ok(())\n}\n\nasync fn fetch_access_token(\n    client: &reqwest::Client,\n    file: &Option<String>,\n) -> Result<(String, i64)> {\n    let credentials = load_adc(file).await?;\n    let value: Value = client\n        .post(\"https://oauth2.googleapis.com/token\")\n        .json(&credentials)\n        .send()\n        .await?\n        .json()\n        .await?;\n","sourceCodeStart":443,"sourceCodeEnd":479,"githubUrl":"https://github.com/sigoden/aichat/blob/82976d349ad97ac9aae0655ad631dace5e2a6385/src/client/vertexai.rs#L443-L479","documentation":"In src/client/vertexai.rs `prepare_gcloud_access_token`, the `expires_in` value returned from fetching a Google access token could not be converted into a valid `Duration` (e.g. it was zero, negative, or overflowed). The library throws this instead of caching an invalid token expiry.","triggerScenarios":"`fetch_access_token` returns an `expires_in` that `Duration::try_seconds` rejects (None): typically 0, negative, or an implausible value parsed from the token endpoint / ADC refresh response.","commonSituations":"Misconfigured ADC refresh token causing a degenerate token response; a fake/mock ADC file; clock or response-shape anomalies from the OAuth endpoint.","solutions":["Re-run `gcloud auth application-default login` to regenerate a valid application_default_credentials.json.","Check the ADC file's refresh_token/client_id/client_secret are real values, not placeholders.","Capture the raw token endpoint response to see what expires_in is actually being returned."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Check ADC validity before calling the provider\nlet adc = std::path::Path::new(&format!(\"{}/.config/gcloud/application_default_credentials.json\", home));\nassert!(adc.exists(), \"run: gcloud auth application-default login\");","typeGuard":null,"tryCatchPattern":"// Rust\ndo_vertexai_call().await.map_err(|e| {\n    if e.to_string().contains(\"expires_in\") { /* re-auth: gcloud auth application-default login */ }\n    e\n})?;","preventionTips":["Refresh ADC credentials periodically with gcloud.","Never hand-craft ADC files with dummy values.","Monitor token-endpoint responses if behind a custom proxy."],"tags":["oauth","token","vertexai","gcp"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"82976d349ad97ac9aae0655ad631dace5e2a6385","analyzedAt":"2026-09-09T18:33:06.139Z","contentChangedAt":"2026-09-09T18:33:06.139Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}