JuliusBrussee/caveman · error

awscreds: unsupported IMDS endpoint scheme

Error message

awscreds: unsupported IMDS endpoint scheme %q

What it means

Error from checkIMDSEndpoint: AWS_EC2_METADATA_SERVICE_ENDPOINT parsed as a URL but its scheme is neither https nor plain http (and http is only allowed for allowlisted plaintext hosts). Because this endpoint is dialled by the proxy-ignoring link client, non-conforming schemes are refused before any IMDSv2 token is attached.

Solutions

  1. Set the endpoint with an explicit scheme, e.g. http://169.254.169.254 or https://....
  2. Use the canonical endpoint http://169.254.169.254/latest unless you have a simulator.
  3. Unset AWS_EC2_METADATA_SERVICE_ENDPOINT to use the built-in default.
  4. Check for stray whitespace/characters corrupting the URL scheme.

Example fix

// before
os.Setenv("AWS_EC2_METADATA_SERVICE_ENDPOINT", "169.254.169.254/latest")
// after
os.Setenv("AWS_EC2_METADATA_SERVICE_ENDPOINT", "http://169.254.169.254/latest")
Defensive patterns

Strategy: validation

Validate before calling

if ep := os.Getenv("AWS_EC2_METADATA_SERVICE_ENDPOINT"); ep != "" {
    u, err := url.Parse(ep)
    if err != nil || (u.Scheme != "http" && u.Scheme != "https") {
        return fmt.Errorf("AWS_EC2_METADATA_SERVICE_ENDPOINT must include an http(s) scheme: %q", ep)
    }
}

Try / catch

if err != nil && strings.Contains(err.Error(), "unsupported IMDS endpoint scheme") {
    log.Fatal("set AWS_EC2_METADATA_SERVICE_ENDPOINT to e.g. http://169.254.169.254")
}

Prevention

When it happens

Trigger: AWS_EC2_METADATA_SERVICE_ENDPOINT contains an unsupported or missing scheme (e.g. 'metadata.internal' with no scheme, or socks5://...), so url.Parse yields a scheme falling into the default branch.

Common situations: Forgetting the http:// prefix when setting the endpoint env var; pasting a proxy or socket URI into the metadata endpoint variable; IMDSv2-only tooling emitting exotic schemes.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/fe19fe95ac0e2b3a. Report an issue: GitHub.

Appendix: source

Thrown at shared/platform/awscreds/awscreds.go:533

// 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.
func checkIMDSEndpoint(raw string) error {
	u, err := url.Parse(raw)
	if err != nil || u.Host == "" {
		return errors.New("awscreds: AWS_EC2_METADATA_SERVICE_ENDPOINT is not a valid URL")
	}
	switch u.Scheme {
	case "https":
		return nil
	case "http":
		if plaintextHostAllowed(u.Hostname(), imdsHosts) {
			return nil
		}
		return fmt.Errorf("awscreds: refusing plaintext IMDS endpoint at host %q (allowed: loopback, 169.254.169.254, fd00:ec2::254)", u.Hostname())
	default:
		return fmt.Errorf("awscreds: unsupported IMDS endpoint scheme %q", u.Scheme)
	}
}

func (p *Provider) fromIMDS(ctx context.Context) (*result, error) {
	if strings.EqualFold(p.env("AWS_EC2_METADATA_DISABLED"), "true") {
		return nil, nil
	}
	base := p.env("AWS_EC2_METADATA_SERVICE_ENDPOINT")
	if base == "" {
		base = defaultIMDSBase
	}
	if err := checkIMDSEndpoint(base); err != nil {
		return nil, err
	}
	base = strings.TrimSuffix(base, "/")

	// IMDSv2 only: a v1 fallback would leave the proxy vulnerable to the SSRF
	// class the session token exists to close.

View on GitHub (pinned to 3ee70a1026)