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
- Set the value to just the path portion starting with exactly one slash, e.g. AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=/v2/credentials/<uuid>.
- If you have the full URL (including http://169.254.170.2), use AWS_CONTAINER_CREDENTIALS_FULL_URI instead.
- Inside ECS, prefer inheriting the variable the agent injects rather than rewriting it.
- 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
- Copy only the path portion into RELATIVE_URI; full URLs belong in FULL_URI.
- Log the resolved env vars in container entrypoints to catch template mangling early.
- In ECS, prefer the agent-injected value over hand-set copies.
- Add a startup assertion for the env var format in task definitions that use it.
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
- ${source} response has a missing or invalid Expiration.
- ROBOMP_BOT_LOGIN must be a non-empty GitHub login
- Unsupported language '{value}'. Supported: {}
- Unable to infer language from file extension: {}. Specify `l
- Invalid pattern: {err}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/e6827c9380df15c3.
Report an issue: GitHub.