sigoden/aichat · error · anyhow::Error
Failed to parse expires_in of access_token
Error message
Failed to parse expires_in of access_token
What it means
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.
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.
Defensive patterns
Strategy: validation
Validate before calling
// Check ADC validity before calling the provider
let adc = std::path::Path::new(&format!("{}/.config/gcloud/application_default_credentials.json", home));
assert!(adc.exists(), "run: gcloud auth application-default login"); Try / catch
// Rust
do_vertexai_call().await.map_err(|e| {
if e.to_string().contains("expires_in") { /* re-auth: gcloud auth application-default login */ }
e
})?; Prevention
- Refresh ADC credentials periodically with gcloud.
- Never hand-craft ADC files with dummy values.
- Monitor token-endpoint responses if behind a custom proxy.
When it happens
Trigger: `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.
Common situations: Misconfigured ADC refresh token causing a degenerate token response; a fake/mock ADC file; clock or response-shape anomalies from the OAuth endpoint.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
AI-assisted analysis of sigoden/aichat@82976d349a (2026-09-09).
Data as JSON: /api/errors/f66d32a144e6d142.
Report an issue: GitHub.
Appendix: source
Thrown at src/client/vertexai.rs:461
Ok(ModelCategory::Mistral)
} else {
unsupported_model!(s)
}
}
}
pub async fn prepare_gcloud_access_token(
client: &reqwest::Client,
client_name: &str,
adc_file: &Option<String>,
) -> Result<()> {
if !is_valid_access_token(client_name) {
let (token, expires_in) = fetch_access_token(client, adc_file)
.await
.with_context(|| "Failed to fetch access token")?;
let expires_at = Utc::now()
+ Duration::try_seconds(expires_in)
.ok_or_else(|| anyhow!("Failed to parse expires_in of access_token"))?;
set_access_token(client_name, token, expires_at.timestamp())
}
Ok(())
}
async fn fetch_access_token(
client: &reqwest::Client,
file: &Option<String>,
) -> Result<(String, i64)> {
let credentials = load_adc(file).await?;
let value: Value = client
.post("https://oauth2.googleapis.com/token")
.json(&credentials)
.send()
.await?
.json()
.await?;
View on GitHub (pinned to 82976d349a)