{"record":{"id":"e73089dd8ed8a184","repo":"JuliusBrussee/caveman","slug":"awscreds-s-returned-incomplete-credentials","errorCode":null,"errorMessage":"awscreds: %s returned incomplete credentials","messagePattern":"awscreds: (.+?) returned incomplete credentials","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/awscreds/awscreds.go","lineNumber":270,"sourceCode":"type result struct {\n\tcreds   awssig.Credentials\n\texpires time.Time\n\tsource  string\n}\n\nfunc (p *Provider) fetch(ctx context.Context) (*result, error) {\n\tfor _, source := range []func(context.Context) (*result, error){\n\t\tp.fromEnv, p.fromWebIdentity, p.fromContainer, p.fromIMDS,\n\t} {\n\t\tres, err := source(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif res == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif !res.creds.Valid() {\n\t\t\treturn nil, fmt.Errorf(\"awscreds: %s returned incomplete credentials\", res.source)\n\t\t}\n\t\treturn res, nil\n\t}\n\treturn nil, errors.New(\"awscreds: no AWS credentials found (env, web identity, container, IMDS)\")\n}\n\nfunc (p *Provider) env(name string) string { return strings.TrimSpace(p.getenv(name)) }\n\n// fromEnv reads static keys. A half-configured pair is an error, not a skip:\n// the operator clearly meant to sign as these keys, and falling through would\n// silently sign as whatever ambient role the host carries — a different\n// principal, bill, and CloudTrail identity — with no disclosure. A lone\n// AWS_SESSION_TOKEN is not a pair and does not trigger this.\nfunc (p *Provider) fromEnv(context.Context) (*result, error) {\n\taccess, secret := p.env(\"AWS_ACCESS_KEY_ID\"), p.env(\"AWS_SECRET_ACCESS_KEY\")\n\tif access == \"\" && secret == \"\" {\n\t\treturn nil, nil\n\t}","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/awscreds/awscreds.go#L252-L288","documentation":"awscreds.fetch iterates its credential sources (env, web identity, container, IMDS) and returns the first result. If a source returns a non-nil result whose awssig.Credentials fail Valid() — i.e. missing AccessKeyID, SecretAccessKey, or SessionToken where required — the whole fetch fails with this error instead of returning half-usable credentials.","triggerScenarios":"Credentials() -> fetch() -> some source (e.g. fromWebIdentity, fromContainer, fromIMDS) parses a response and builds a result whose creds.Valid() is false: empty access key ID, empty secret, or a required session token missing (e.g. role-credential responses missing SessionToken).","commonSituations":"An IMDS/container metadata endpoint returns malformed or partial JSON; the STS AssumeRoleWithWebIdentity XML parse yields empty fields; a corporate proxy intercepts metadata requests and returns stub data; env vars set partially (only AWS_ACCESS_KEY_ID set, no secret).","solutions":["Check which source produced it — the %s verb names it (web identity, container, IMDS) — and inspect that source's raw response","Verify environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY (and AWS_SESSION_TOKEN if using session creds) are all set and non-empty","Test the metadata endpoint directly (169.254.169.254 / AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) from the same host; a proxy or firewall may be returning junk","Prefer an explicit, complete source: set full static creds in env or use a valid web identity token file so a healthy source wins"],"exampleFix":"// before\ncreds, err := awscreds.Credentials(ctx, p) // \"awscreds: imds returned incomplete credentials\"\n// after\nif os.Getenv(\"AWS_ACCESS_KEY_ID\") == \"\" || os.Getenv(\"AWS_SECRET_ACCESS_KEY\") == \"\" {\n    // fix partial env config before calling\n    log.Fatal(\"set both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY\")\n}\ncreds, err := awscreds.Credentials(ctx, p)","handlingStrategy":"fallback","validationCode":"// sanity-check env creds before relying on the chain\nid, sec := os.Getenv(\"AWS_ACCESS_KEY_ID\"), os.Getenv(\"AWS_SECRET_ACCESS_KEY\")\nif id != \"\" && sec == \"\" { return errors.New(\"AWS_SECRET_ACCESS_KEY missing while AWS_ACCESS_KEY_ID set\") }","typeGuard":"func credsComplete(err error) bool {\n    return err != nil && !strings.Contains(err.Error(), \"returned incomplete credentials\")\n}","tryCatchPattern":"creds, err := awscreds.Credentials(ctx, p)\nif err != nil {\n    if strings.Contains(err.Error(), \"incomplete credentials\") {\n        // fall back to an explicit static/profile provider\n        creds = staticCredsFromProfile(\"default\")\n    }\n    if creds == nil { return fmt.Errorf(\"aws credentials: %w\", err) }\n}","preventionTips":["Never set AWS_ACCESS_KEY_ID without AWS_SECRET_ACCESS_KEY (and AWS_SESSION_TOKEN when applicable)","Audit metadata endpoints (IMDS/container) on hosts where proxies run — stub responses cause this","Prefer one explicit source over the implicit chain in production","Log which source the chain selected to spot degraded sources early"],"tags":["aws","credentials","authentication","go"],"backgroundTag":"missing-credentials","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}