JuliusBrussee/caveman · error

awscreds: AWS_EC2_METADATA_SERVICE_ENDPOINT is not a valid…

Error message

awscreds: AWS_EC2_METADATA_SERVICE_ENDPOINT is not a valid URL

What it means

checkIMDSEndpoint validates AWS_EC2_METADATA_SERVICE_ENDPOINT: it must parse as a URL with a non-empty host, and be https or http to loopback/link-local IMDS addresses. This guard exists because the variable was previously dialed verbatim by the proxy-ignoring IMDS client, so any host named there became a proxy-bypassing request carrying the IMDSv2 token. A value that fails to parse or has an empty host yields this error.

Solutions

  1. Set AWS_EC2_METADATA_SERVICE_ENDPOINT to a full URL including scheme and host, e.g. http://169.254.169.254/latest or https://custom-endpoint
  2. Check shell/export quoting so the value is not truncated or space-split
  3. Remove the variable entirely to use the default IMDS endpoint

Example fix

// before
export AWS_EC2_METADATA_SERVICE_ENDPOINT=169.254.169.254
// after
export AWS_EC2_METADATA_SERVICE_ENDPOINT=http://169.254.169.254
Defensive patterns

Strategy: validation

Validate before calling

func imdsEndpointIsValid(raw string) bool {
	u, err := url.Parse(raw)
	if err != nil || u.Host == "" { return false }
	if u.Scheme == "https" { return true }
	if u.Scheme == "http" {
		h := u.Hostname()
		return h == "169.254.169.254" || h == "fd00:ec2::254" || h == "localhost" || strings.HasPrefix(h, "127.")
	}
	return false
}

Try / catch

if ep := os.Getenv("AWS_EC2_METADATA_SERVICE_ENDPOINT"); ep != "" {
	if err := awscreds.CheckIMDSEndpoint(ep); err != nil {
		// unset or correct the variable before credential resolution
	}
}

Prevention

When it happens

Trigger: fromIMDS runs while AWS_EC2_METADATA_SERVICE_ENDPOINT is unset-but-nonempty-garbage, contains only a scheme with no host ('http://'), or otherwise fails url.Parse.

Common situations: Setting the endpoint to a bare hostname without scheme, truncated env values from misquoted shell exports, or an endpoint config written as '169.254.169.254' instead of 'http://169.254.169.254'.

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


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

Appendix: source

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

		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.
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
	}

View on GitHub (pinned to 3ee70a1026)