JuliusBrussee/caveman · error
awscreds: AWS_CONTAINER_CREDENTIALS_FULL_URI is not a valid…
Error message
awscreds: AWS_CONTAINER_CREDENTIALS_FULL_URI is not a valid URL
What it means
checkContainerURI validates AWS_CONTAINER_CREDENTIALS_FULL_URI before it is ever dialed: it must parse as a URL and be either https, or http pointed at loopback or the fixed ECS/EKS credential endpoints (169.254.170.2 / 169.254.170.23). This prevents handing a task role's Authorization token to an arbitrary attacker-controlled host. A value that does not parse as a URL yields this error.
Solutions
- Fix AWS_CONTAINER_CREDENTIALS_FULL_URI to a syntactically valid absolute URL (e.g. http://169.254.170.2/v2/credentials or an https:// endpoint)
- Check the task definition / env injection for quoting or whitespace issues corrupting the value
- Prefer the relative variant AWS_CONTAINER_CREDENTIALS_RELATIVE_URI on ECS, which avoids full-URL validation entirely
Example fix
// before export AWS_CONTAINER_CREDENTIALS_FULL_URI="http://169.254.170.2 /v2/credentials" // after export AWS_CONTAINER_CREDENTIALS_FULL_URI="http://169.254.170.2/v2/credentials"
Defensive patterns
Strategy: validation
Validate before calling
func containerURIIsSafe(raw string) bool {
u, err := url.Parse(raw)
if err != nil { return false }
if u.Scheme == "https" { return true }
if u.Scheme == "http" {
h := u.Hostname()
return h == "169.254.170.2" || h == "169.254.170.23" || h == "localhost" || strings.HasPrefix(h, "127.")
}
return false
}
Try / catch
if err := awscreds.CheckContainerURI(os.Getenv("AWS_CONTAINER_CREDENTIALS_FULL_URI")); err != nil {
// fix env before starting the provider
}
Prevention
- Use AWS_CONTAINER_CREDENTIALS_RELATIVE_URI on ECS instead of the full URI
- Copy endpoints exactly from task-definition metadata, no manual retyping
- Beware shell quoting that injects spaces into env values
When it happens
Trigger: fromContainer runs and AWS_CONTAINER_CREDENTIALS_FULL_URI contains a string url.Parse rejects — control characters, stray spaces, or a malformed scheme like 'htp://...' or '%zz'.
Common situations: Typoed scheme in an ECS/EKS task definition env var, quoting bugs injecting whitespace into the env value, or copying an example endpoint with a trailing character.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- awscreds: AWS_EC2_METADATA_SERVICE_ENDPOINT is not a valid…
- awscreds: refusing plaintext container credentials endpoint…
- awscreds: refusing plaintext IMDS endpoint at host
- githubapp: request path escaped configured host
- --instance requires a private HTTPS origin (HTTP loopback…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/61baa6b9c526261a.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/awscreds/awscreds.go:500
return true
}
addr = addr.Unmap()
for _, allowed := range allow {
if addr == allowed {
return true
}
}
return false
}
// checkContainerURI applies the SDK rule for a caller-supplied credential
// endpoint: TLS anywhere, plaintext only to loopback or the fixed ECS/EKS
// credential addresses. Without it, AWS_CONTAINER_CREDENTIALS_FULL_URI is a
// request to hand a task role's Authorization token to an arbitrary host.
func checkContainerURI(raw string) error {
u, err := url.Parse(raw)
if err != nil {
return errors.New("awscreds: AWS_CONTAINER_CREDENTIALS_FULL_URI is not a valid URL")
}
switch u.Scheme {
case "https":
return nil
case "http":
if plaintextHostAllowed(u.Hostname(), containerCredentialHosts) {
return nil
}
return fmt.Errorf("awscreds: refusing plaintext container credentials endpoint at host %q (allowed: loopback, 169.254.170.2, 169.254.170.23, fd00:ec2::23)", u.Hostname())
default:
return fmt.Errorf("awscreds: unsupported container credentials scheme %q", u.Scheme)
}
}
// checkIMDSEndpoint is checkContainerURI for AWS_EC2_METADATA_SERVICE_ENDPOINT.
// That variable was taken verbatim and then dialled with p.link — the client
// that deliberately ignores every proxy setting — so any host named there became
// a proxy-bypassing outbound request with the IMDSv2 token attached.View on GitHub (pinned to 3ee70a1026)