can1357/oh-my-pi · error · AIError.AwsCredentialsError

assume-role

assume-role

Error message

AWS AssumeRole failed: ${response.status} ${xmlTag(xml, "Message") ?? xml.slice(0, 200)}

What it means

This error is thrown when an AWS STS AssumeRole API call made while resolving a profile's role_arn returns a non-OK HTTP status. The library SigV4-signs the request with the base (source) credentials and posts it to the regional STS endpoint; STS rejected the exchange, so no temporary role credentials can be returned. The message embeds the HTTP status and the STS error Message extracted from the XML response body (or the first 200 chars of raw XML).

Source

Thrown at packages/ai/src/providers/aws-credentials.ts:397

	const signed = await signRequest({
		method: "POST",
		host: endpoint.host,
		path: endpoint.pathname,
		body: payload,
		region,
		service: "sts",
		credentials: base,
		headers: { "content-type": contentType },
	});
	const response = await fetchImpl(endpoint, {
		method: "POST",
		headers: { ...signed, "content-type": contentType },
		body: payload,
		signal,
	});
	const xml = await response.text();
	if (!response.ok) {
		throw new AIError.AwsCredentialsError(
			`AWS AssumeRole failed: ${response.status} ${xmlTag(xml, "Message") ?? xml.slice(0, 200)}`,
			"assume-role",
		);
	}
	return parseStsCredentials(xml, "AWS AssumeRole", "assume-role");
}

interface SsoCachedToken {
	accessToken?: string;
	expiresAt?: string;
	startUrl?: string;
	region?: string;
}

async function readSsoCredentials(
	profileCfg: Record<string, string>,
	configIni: AwsIniFile | undefined,
	defaultRegion: string,

View on GitHub (pinned to 9690622007)

Solutions

  1. Read the embedded STS Message in the error: AccessDenied -> fix IAM/trust policy, ExternalId -> align profile external_id with trust policy, ExpiredToken -> refresh base credentials
  2. Verify role_arn, external_id, duration_seconds, and source_profile/credential_source in ~/.aws/config
  3. Confirm the base credentials are valid: aws sts get-caller-identity with those credentials
  4. Confirm the IAM identity has sts:AssumeRole allowed on the target role and the role's trust policy allows the source principal
  5. Test the same chain with `aws sts assume-role --role-arn ...` to confirm it's a config issue, not library-specific

Example fix

// before (~/.aws/config)
[profile deploy]
role_arn = arn:aws:iam::123456789012:role/Deploy
source_profile = base
duration_seconds = 72000 // invalid: max 43200

// after
[profile deploy]
role_arn = arn:aws:iam::123456789012:role/Deploy
source_profile = base
duration_seconds = 3600
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check base credentials are live before assuming the role:
import { $ } from "bun";
const r = await $`aws sts get-caller-identity`.quiet().nothrow();
if (r.exitCode !== 0) throw new Error("Base AWS credentials invalid; fix source_profile/keys before AssumeRole");

Type guard

function isAssumeRoleError(err: unknown): err is Error & { code: string } {
  return err instanceof Error && "code" in err && (err as { code?: string }).code === "assume-role";
}

Try / catch

try {
  creds = await resolveProfileChain(profile);
} catch (err) {
  if (isAssumeRoleError(err)) {
    // message embeds HTTP status + STS Message; surface to user with profile name
    logger.error("STS AssumeRole rejected", { profile, detail: err.message });
  }
  throw err;
}

Prevention

When it happens

Trigger: stsAssumeRole() receives response.ok === false from the STS endpoint. Typical causes: base credentials lack sts:AssumeRole permission on the target role (AccessDenied), wrong role_arn, invalid/mismatched external_id, duration_seconds outside 900-43200, expired or untrusted source credentials (ExpiredToken/InvalidClientTokenId), or wrong region for the STS endpoint.

Common situations: Cross-account role assumption where the trust policy's ExternalId doesn't match the profile's external_id; IAM policy missing sts:AssumeRole on the role ARN; typos in role_arn in ~/.aws/config; using a region whose STS endpoint the account hasn't enabled; stale long-lived keys rotated or revoked; role trust policy not listing the source principal.

Related errors


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