-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Shell command (\!) API keys are cached forever, causing expired token errors with authHeader #1835
Description
Description
When using authHeader: true with a shell command (!) for apiKey (e.g., Azure AD tokens via az cli), the token is fetched once at startup and never refreshed. Sessions that outlast the token's lifetime (typically ~60 minutes for Azure) fail with expired token errors, requiring a full restart of pi.
Configuration
{
"providers": {
"azure-foundry": {
"baseUrl": "https://example.services.ai.azure.com/anthropic",
"api": "anthropic-messages",
"apiKey": "!az account get-access-token --resource https://cognitiveservices.azure.com --query accessToken -o tsv",
"authHeader": true,
"models": [...]
}
}
}Root Cause
Three things combine to make this unfixable without restarting:
-
resolve-config-value.tscaches shell command results in aMapwith no TTL. Once cached, the same value is returned for the lifetime of the process:const commandResultCache = new Map<string, string | undefined>(); // ... if (commandResultCache.has(commandConfig)) { return commandResultCache.get(commandConfig); // stale forever }
-
ModelRegistry.refresh()does not callclearConfigValueCache()before reloading models, so even opening/modeland re-selecting doesn't help — the cache still returns the old token. -
authHeader: trueresolves the API key and bakes it into the model's staticheaders.Authorizationat parse time inparseModels(). There's no dynamic re-resolution at request time.
Expected Behavior
Users should never see an expired token error during a session. Token refresh should be completely transparent — no manual intervention, no /model re-selection, no restart.
Suggested Fix
Add a cacheTtl field (in seconds) to the provider configuration for ! shell commands:
{
"providers": {
"azure-foundry": {
"apiKey": "!az account get-access-token --resource https://cognitiveservices.azure.com --query accessToken -o tsv",
"cacheTtl": 1800,
...
}
}
}When cacheTtl is set, the ! command result should be re-executed automatically after the TTL expires. The re-resolution needs to happen at request time — not just at model parse time — so that authHeader headers are always current. This way the token stays fresh transparently and the user never encounters an auth error from an expired token.
Without a cacheTtl, the current cache-forever behavior is fine for static secrets that don't expire.
Docs Note
The models.md documentation says:
The file reloads each time you open
/model. Edit during session; no restart needed.
This is misleading for ! commands since the command cache prevents actual re-execution even when the file is re-read. It probably makes sense to also alter the logic/model to clear cache entries and refresh them. This could serve as a minimal fix for this issue, but the devx is not as nice as the full fix proposed above