JuliusBrussee/caveman · error
awscreds: returned an unparseable response
Error message
awscreds: %s returned an unparseable response
What it means
credentialsFromJSON unmarshals the body returned by the ECS container metadata endpoint or EC2 IMDS into credentialJSON. If json.Unmarshal fails, the response was not the expected credential JSON object, and the error reports 'unparseable response' for the source (container or imds). The library throws this because continuing without validating the payload shape would produce zero-valued credentials.
Solutions
- Log/capture the raw body (temporarily) to see what the endpoint actually returned; an HTML page indicates interception by a proxy or wrong endpoint.
- Verify AWS_CONTAINER_CREDENTIALS_RELATIVE_URI points to the ECS agent's credentials path and that the ecs agent is running.
- If AWS_EC2_METADATA_SERVICE_ENDPOINT is overridden, reset it to http://169.254.169.254 and retry.
- Ensure no proxy env vars (HTTP_PROXY) route metadata traffic; use NO_PROXY=169.254.169.254.
Defensive patterns
Strategy: validation
Validate before calling
func looksLikeJSONBody(b []byte) bool {
t := bytes.TrimSpace(b)
return len(t) > 0 && (t[0] == '{' || t[0] == '"')
} Try / catch
body, err := p.doJSON(client, req, "imds credentials")
if err != nil {
return err
}
if !looksLikeJSONBody(body) {
return fmt.Errorf("metadata endpoint returned non-JSON (%d bytes); check proxy/NO_PROXY", len(body))
} Prevention
- Set NO_PROXY=169.254.169.254 and exclude the metadata host from HTTP_PROXY.
- Point AWS_CONTAINER_CREDENTIALS_RELATIVE_URI only at the ECS agent path.
- Do not override AWS_EC2_METADATA_SERVICE_ENDPOINT unless testing a real IMDS-compatible service.
When it happens
Trigger: fromContainer or fromIMDS receives a 2xx body that is not valid JSON: an HTML error/login page from a proxy, a plain-text message, an empty body, or truncation at the maxBody limit.
Common situations: A corporate proxy or VPN captive portal intercepting requests to 169.254.169.254 and returning an HTML page with 200 OK; AWS_CONTAINER_CREDENTIALS_RELATIVE_URI pointing to a non-metadata HTTP service; custom AWS_EC2_METADATA_SERVICE_ENDPOINT pointing at the wrong server.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- awscreds: credential expiry
- awscreds: : http
- awscreds: returned code
- AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be set…
- awscreds: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/d444323297aafa41.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/awscreds/awscreds.go:621
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("awscreds: %s request failed: %w", what, err)
}
defer resp.Body.Close()
body, err := io.ReadAll(io.LimitReader(resp.Body, maxBody))
if err != nil {
return nil, fmt.Errorf("awscreds: read %s response: %w", what, err)
}
if resp.StatusCode < 200 || resp.StatusCode > 299 {
return nil, fmt.Errorf("awscreds: %s: http %d", what, resp.StatusCode)
}
return body, nil
}
func credentialsFromJSON(body []byte, source string) (*result, error) {
var parsed credentialJSON
if err := json.Unmarshal(body, &parsed); err != nil {
return nil, fmt.Errorf("awscreds: %s returned an unparseable response", source)
}
if parsed.Code != "" && !strings.EqualFold(parsed.Code, "Success") {
return nil, fmt.Errorf("awscreds: %s returned code %q", source, parsed.Code)
}
expires, err := parseExpiry(parsed.Expiration)
if err != nil {
return nil, fmt.Errorf("awscreds: %s credential expiry: %w", source, err)
}
return &result{
creds: awssig.Credentials{
AccessKeyID: strings.TrimSpace(parsed.AccessKeyID),
SecretAccessKey: strings.TrimSpace(parsed.SecretAccessKey),
SessionToken: strings.TrimSpace(parsed.Token),
},
expires: expires,
source: source,
}, nil
}View on GitHub (pinned to 3ee70a1026)