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

container

container

Error message

AWS_CONTAINER_CREDENTIALS_RELATIVE_URI must be a single-host absolute path.

What it means

ECS task credential resolution reads AWS_CONTAINER_CREDENTIALS_RELATIVE_URI, which per the AWS contract must be a path like /v2/credentials/... that is appended to http://169.254.170.2. The library rejects values that do not start with a single "/" (or start with "//", which URL parsing would treat as protocol-relative host) so it never builds a bogus endpoint.

Source

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

	AccessKeyId?: string;
	SecretAccessKey?: string;
	Token?: string;
	Expiration?: string;
}

const ECS_TASK_CREDENTIALS_BASE_URL = new URL("http://169.254.170.2/");

async function readContainerCredentials(
	signal: AbortSignal | undefined,
	fetchImpl: FetchImpl,
): Promise<ResolvedCredentials | undefined> {
	const relativeUri = $env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI;
	const fullUri = $env.AWS_CONTAINER_CREDENTIALS_FULL_URI;
	if (!relativeUri && !fullUri) return undefined;
	let endpoint: URL;
	if (relativeUri) {
		if (!relativeUri.startsWith("/") || relativeUri.startsWith("//")) {
			throw new AIError.AwsCredentialsError(
				"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI must be a single-host absolute path.",
				"container",
			);
		}
		endpoint = new URL(relativeUri.slice(1), ECS_TASK_CREDENTIALS_BASE_URL);
	} else {
		try {
			endpoint = new URL(fullUri as string);
		} catch (err) {
			throw new AIError.AwsCredentialsError(
				`AWS_CONTAINER_CREDENTIALS_FULL_URI is invalid: ${String(err)}`,
				"container",
				{ cause: err },
			);
		}
		if (endpoint.protocol !== "https:" && !isLocalOrMetadataHost(endpoint.hostname)) {
			throw new AIError.AwsCredentialsError(
				"AWS_CONTAINER_CREDENTIALS_FULL_URI must use HTTPS or a local metadata host.",

View on GitHub (pinned to 9690622007)

Solutions

  1. Set the value to just the path portion starting with exactly one slash, e.g. AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=/v2/credentials/<uuid>.
  2. If you have the full URL (including http://169.254.170.2), use AWS_CONTAINER_CREDENTIALS_FULL_URI instead.
  3. Inside ECS, prefer inheriting the variable the agent injects rather than rewriting it.
  4. Log/echo the env var in the container to see what was actually passed and fix the templating that mangled it.

Example fix

// before
AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=http://169.254.170.2/v2/credentials/6d1a-...

// after
AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=/v2/credentials/6d1a-...
Defensive patterns

Strategy: validation

Validate before calling

function validRelativeUri(v) {
  return typeof v === "string" && v.startsWith("/") && !v.startsWith("//");
}
const rel = process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI;
if (rel && !validRelativeUri(rel)) {
  throw new Error(`AWS_CONTAINER_CREDENTIALS_RELATIVE_URI must start with a single '/': ${rel}`);
}

Try / catch

try {
  return await resolveAwsCredentials();
} catch (err) {
  if (err instanceof AIError.AwsCredentialsError && err.code === "container" && /must be a single-host absolute path/.test(err.message)) {
    logger.error("Fix AWS_CONTAINER_CREDENTIALS_RELATIVE_URI to a path like /v2/credentials/<id>, or use FULL_URI");
    throw err;
  }
  throw err;
}

Prevention

When it happens

Trigger: resolveContainerCredentials runs because AWS_CONTAINER_CREDENTIALS_RELATIVE_URI is set, and the value is empty-of-slash, e.g. "v2/credentials/uuid", "http://...", or "//169.254.170.2/...".

Common situations: Hand-copied the full ECS agent URL into the RELATIVE var instead of the FULL_URI one; leading slash stripped by a container orchestrator template; env var interpolated with the host portion included; using credentials from an ECS task metadata endpoint copied verbatim.

Related errors


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