can1357/oh-my-pi · error · AIError.AwsCredentialsError
sso-token-expired
sso-token-expired
Error message
AWS SSO token for ${startUrl} has expired. Run 'aws sso login' to refresh. What it means
Thrown when a cached AWS SSO token exists in ~/.aws/sso/cache but its expiresAt timestamp is in the past. The library validates the token freshness before using it as a bearer token against the SSO portal federation endpoint and refuses to send an expired token, since the API call would fail with Unauthorized.
Source
Thrown at packages/ai/src/providers/aws-credentials.ts:443
if (sessionName && configIni) {
const session = configIni[`sso-session:${sessionName}`];
if (session) {
startUrl = startUrl || session.sso_start_url;
ssoRegion = ssoRegion || session.sso_region;
}
}
if (!startUrl || !ssoRegion) return undefined;
const token = await loadSsoCachedToken(startUrl, sessionName);
if (!token?.accessToken) {
throw new AIError.AwsCredentialsError(
`AWS SSO token for ${startUrl} not found in ~/.aws/sso/cache. Run 'aws sso login' first.`,
"sso-token-missing",
);
}
const expiresAt = token.expiresAt ? Date.parse(token.expiresAt) : Number.POSITIVE_INFINITY;
if (Number.isFinite(expiresAt) && expiresAt <= Date.now()) {
throw new AIError.AwsCredentialsError(
`AWS SSO token for ${startUrl} has expired. Run 'aws sso login' to refresh.`,
"sso-token-expired",
);
}
const url =
`https://portal.sso.${ssoRegion}.amazonaws.com/federation/credentials` +
`?account_id=${encodeURIComponent(profileCfg.sso_account_id)}` +
`&role_name=${encodeURIComponent(profileCfg.sso_role_name)}`;
const response = await fetchImpl(url, {
method: "GET",
headers: { "x-amz-sso_bearer_token": token.accessToken },
signal,
});
if (!response.ok) {
const body = await response.text().catch(() => "");
throw new AIError.AwsCredentialsError(
`AWS SSO GetRoleCredentials failed: ${response.status} ${body.slice(0, 200)}`,View on GitHub (pinned to 9690622007)
Solutions
- Run `aws sso login --profile <profile>` to obtain a fresh token, then retry
- Increase the SSO session duration in AWS IAM Identity Center / permission set settings if sessions expire too often
- For long-running processes, re-resolve credentials near expiry or wrap resolution to re-login/retry on this error
- Clear stale entries in ~/.aws/sso/cache only if the CLI doesn't overwrite them on login
Example fix
// before $ ./app --profile corp # AWS SSO token ... has expired // after $ aws sso login --profile corp $ ./app --profile corp
Defensive patterns
Strategy: validation
Validate before calling
import * as fs from "node:fs";
import * as path from "node:path";
import * as os from "node:os";
// Pre-check token freshness before resolving:
function ssoTokenFresh(): boolean {
const dir = path.join(os.homedir(), ".aws", "sso", "cache");
try {
return fs.readdirSync(dir).some(f => {
try {
const t = JSON.parse(fs.readFileSync(path.join(dir, f), "utf8"));
return !!t.accessToken && (!t.expiresAt || Date.parse(t.expiresAt) > Date.now());
} catch { return false; }
});
} catch { return false; }
}
if (!ssoTokenFresh()) throw new Error("SSO token expired; run `aws sso login`"); Type guard
function tokenIsValid(t: { expiresAt?: string }): boolean {
const exp = t.expiresAt ? Date.parse(t.expiresAt) : Number.POSITIVE_INFINITY;
return Number.isFinite(exp) ? exp > Date.now() : true;
} Try / catch
try {
creds = await resolveProfileChain(profile);
} catch (err) {
if (err instanceof Error && /has expired/.test(err.message)) {
await $`aws sso login --profile ${profile}`;
creds = await resolveProfileChain(profile); // retry once with fresh token
} else throw err;
} Prevention
- Re-run `aws sso login` at the start of each work session or before long jobs
- Increase SSO session duration in IAM Identity Center if it expires too often
- For long-running processes, schedule token refresh before the cached expiresAt
- Don't cache resolved SSO credentials beyond their expiration
When it happens
Trigger: readSsoCredentials() loads the cached token, parses token.expiresAt, and Date.parse(expiresAt) <= Date.now() — i.e. the SSO session created by `aws sso login` has lapsed (typically after 8-12 hours or the configured session duration).
Common situations: Resuming work the next day after the prior `aws sso login` expired; long-running processes holding a resolved profile past expiry; CI caches a token that expires mid-pipeline; organization shortened the SSO session duration.
Related errors
- sso-token-missing
- sso-role
- assume-role
- Failed to load tree-sitter language: {err}
- Bedrock HTTP ${response.status}: ${errBody.slice(0, 1000)}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/12cb07071c9f969a.
Report an issue: GitHub.