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

  1. Run `aws sso login --profile <profile>` to obtain a fresh token, then retry
  2. Increase the SSO session duration in AWS IAM Identity Center / permission set settings if sessions expire too often
  3. For long-running processes, re-resolve credentials near expiry or wrap resolution to re-login/retry on this error
  4. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/12cb07071c9f969a. Report an issue: GitHub.