sigoden/aichat · error · anyhow::Error
Invalid access token
Error message
Invalid access token
What it means
get_access_token looks up a stored access token for the given client name in the ACCESS_TOKENS map; if no entry exists (never authenticated, expired token removed, or wrong client name), it returns 'Invalid access token'. Callers like prepare_chat_completions/prepare_embeddings need this token to authorize API requests.
Solutions
- Run the authentication/login flow first so a token is stored for this client
- Check that the client_name used for lookup matches the one used when storing the token
- Refresh or re-obtain the access token if it expired and was evicted
- Verify the provider actually requires/uses access-token auth and your config points at the right client
Example fix
// before
let token = get_access_token("claude")?; // never authenticated
// after
ensure_access_token("claude")?; // login/refresh first
let token = get_access_token("claude")?; Defensive patterns
Strategy: validation
Validate before calling
// guard before calling APIs that need the token
if !is_valid_access_token("claude") {
// run login/token-refresh flow first
refresh_access_token("claude")?;
}
let token = get_access_token("claude")?; Type guard
fn token_available(client_name: &str) -> bool { is_valid_access_token(client_name) } Try / catch
let token = match get_access_token(client_name) {
Ok(t) => t,
Err(_) => { perform_login(client_name)?; get_access_token(client_name)? }
}; Prevention
- Always complete the auth/login flow before chat/embedding calls
- Refresh tokens proactively based on the stored expires_at timestamp
- Use a single constant for each client name to avoid lookup mismatches
- Check is_valid_access_token before operations requiring auth
When it happens
Trigger: Calling get_access_token (indirectly via prepare_chat_completions/prepare_embeddings) for a client_name that has no entry in ACCESS_TOKENS — token never obtained, was cleared after expiry, or the name is misspelled.
Common situations: Auth/login step was skipped before making chat/embedding calls; access token expired and was not refreshed; client name mismatch between token storage and lookup; using a client that requires OAuth without completing the token exchange.
Related errors
- {err_msg}
- Failed to parse expires_in of access_token
- Invalid response data
- No application_default_credentials.json
AI-assisted analysis of sigoden/aichat@82976d349a (2026-09-09).
Data as JSON: /api/errors/0c258d3d5296bdfc.
Report an issue: GitHub.
Appendix: source
Thrown at src/client/access_token.rs:15
use anyhow::{anyhow, Result};
use chrono::Utc;
use indexmap::IndexMap;
use parking_lot::RwLock;
use std::sync::LazyLock;
static ACCESS_TOKENS: LazyLock<RwLock<IndexMap<String, (String, i64)>>> =
LazyLock::new(|| RwLock::new(IndexMap::new()));
pub fn get_access_token(client_name: &str) -> Result<String> {
ACCESS_TOKENS
.read()
.get(client_name)
.map(|(token, _)| token.clone())
.ok_or_else(|| anyhow!("Invalid access token"))
}
pub fn is_valid_access_token(client_name: &str) -> bool {
let access_tokens = ACCESS_TOKENS.read();
let (token, expires_at) = match access_tokens.get(client_name) {
Some(v) => v,
None => return false,
};
!token.is_empty() && Utc::now().timestamp() < *expires_at
}
pub fn set_access_token(client_name: &str, token: String, expires_at: i64) {
let mut access_tokens = ACCESS_TOKENS.write();
let entry = access_tokens.entry(client_name.to_string()).or_default();
entry.0 = token;
entry.1 = expires_at;
}
View on GitHub (pinned to 82976d349a)