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

${source} response is missing credentials.

Error message

${source} response is missing credentials.

What it means

After calling AWS STS (AssumeRole or AssumeRoleWithWebIdentity), the library parses the `<Credentials>` block of the XML response for AccessKeyId, SecretAccessKey, and SessionToken. If any of the three tags is missing or empty, the response cannot yield usable credentials and this error is thrown. It is thrown at parse time with the source label (e.g. "AWS web identity") and the caller's error kind.

Source

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

		.replaceAll("&amp;", "&")
		.replaceAll("&lt;", "<")
		.replaceAll("&gt;", ">")
		.replaceAll("&quot;", '"')
		.replaceAll("&apos;", "'");
}

function stsEndpoint(region: string): string {
	const dnsSuffix = region.startsWith("cn-") ? "amazonaws.com.cn" : "amazonaws.com";
	return `https://sts.${region}.${dnsSuffix}/`;
}

/** Parse `<Credentials>` from an STS AssumeRole/WithWebIdentity XML response. */
function parseStsCredentials(xml: string, source: string, kind: AIError.AwsCredentialsErrorKind): ResolvedCredentials {
	const accessKeyId = xmlTag(xml, "AccessKeyId");
	const secretAccessKey = xmlTag(xml, "SecretAccessKey");
	const sessionToken = xmlTag(xml, "SessionToken");
	if (!accessKeyId || !secretAccessKey || !sessionToken) {
		throw new AIError.AwsCredentialsError(`${source} response is missing credentials.`, kind);
	}
	const expiresAt = requireDynamicCredentialExpiration(xmlTag(xml, "Expiration"), source, kind);
	return { accessKeyId, secretAccessKey, sessionToken, expiresAt };
}

async function readWebIdentityCredentials(
	region: string,
	signal: AbortSignal | undefined,
	fetchImpl: FetchImpl,
): Promise<ResolvedCredentials | undefined> {
	const tokenFile = $env.AWS_WEB_IDENTITY_TOKEN_FILE;
	const roleArn = $env.AWS_ROLE_ARN;
	if (!tokenFile || !roleArn) return undefined;
	return assumeRoleWithWebIdentity(
		{ roleArn, tokenFile, sessionName: $env.AWS_ROLE_SESSION_NAME },
		region,
		signal,
		fetchImpl,

View on GitHub (pinned to 9690622007)

Solutions

  1. Log/print the raw STS response body to see what was actually returned; verify it is an <AssumeRole...Response> document containing <Credentials>.
  2. Check for proxy/MITM interference: unset HTTP(S)_PROXY or bypass the proxy for sts.<region>.amazonaws.com and retry.
  3. Verify the region string is correct (stsEndpoint builds sts.<region>.amazonaws.com); a wrong region may reach an unexpected endpoint.
  4. If using a VPC endpoint or custom endpoint, confirm it actually fronts STS and forwards the full response.
  5. Retry the request — transient truncation can yield partial XML.
Defensive patterns

Strategy: retry

Validate before calling

function looksLikeStsSuccess(xml) {
  return /<Credentials>[\s\S]*<AccessKeyId>[\s\S]*<\/Credentials>/.test(xml);
}
// after a raw fetch, inspect before parsing
// if (!looksLikeStsSuccess(xml)) log the body and retry/fail fast

Type guard

function isStsCredentialXml(xml) {
  const tag = (t) => new RegExp(`<${t}>([\\s\\S]*?)</${t}>`).exec(xml)?.[1];
  return Boolean(tag("AccessKeyId") && tag("SecretAccessKey") && tag("SessionToken"));
}

Try / catch

try {
  return await withWebIdentity(region, signal, fetch);
} catch (err) {
  if (err instanceof AIError.AwsCredentialsError && /response is missing credentials/.test(err.message)) {
    logger.error("STS returned non-credential XML — check proxy/region/endpoint", { cause: err });
    throw err;
  }
  throw err;
}

Prevention

When it happens

Trigger: parseStsCredentials receives XML from STS that lacks AccessKeyId, SecretAccessKey, or SessionToken inside <Credentials> — e.g. STS returned an error-shaped or unexpected XML body with a 200 status, a truncated response, or a proxy returning an HTML page.

Common situations: Corporate proxies or captive portals intercepting STS calls and returning non-STS XML; STS API changes or VPC-endpoint error pages returned with HTTP 200; region misconfiguration hitting a non-STS endpoint that responds with a valid-XML-but-different document; responses where tags are attribute-formatted instead of element-formatted.

Related errors


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