{"record":{"id":"30c95ec204b7a188","repo":"JuliusBrussee/caveman","slug":"awscreds-aws-access-key-id-and-aws-secret-access-key-must","errorCode":null,"errorMessage":"awscreds: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must both be set","messagePattern":"awscreds: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must both be set","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/awscreds/awscreds.go","lineNumber":290,"sourceCode":"\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}\n\tif access == \"\" || secret == \"\" {\n\t\treturn nil, errors.New(\"awscreds: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must both be set\")\n\t}\n\treturn &result{\n\t\tcreds: awssig.Credentials{\n\t\t\tAccessKeyID:     access,\n\t\t\tSecretAccessKey: secret,\n\t\t\tSessionToken:    p.env(\"AWS_SESSION_TOKEN\"),\n\t\t},\n\t\tsource: \"env\",\n\t}, nil\n}\n\n// fromWebIdentity implements the EKS IRSA / generic OIDC flow: exchange the\n// projected service account token for role credentials at STS. The call is\n// unsigned by definition — the token is the proof.\nfunc (p *Provider) fromWebIdentity(ctx context.Context) (*result, error) {\n\ttokenFile, roleARN := p.env(\"AWS_WEB_IDENTITY_TOKEN_FILE\"), p.env(\"AWS_ROLE_ARN\")\n\tif tokenFile == \"\" || roleARN == \"\" {\n\t\treturn nil, nil","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/awscreds/awscreds.go#L272-L308","documentation":"fromEnv treats a half-configured static key pair as an error rather than silently skipping: if exactly one of AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY is set, the operator clearly intended those credentials, and signing with a pair missing one half is impossible. Only the env spelling is validated here.","triggerScenarios":"Setting AWS_ACCESS_KEY_ID without AWS_SECRET_ACCESS_KEY (or vice versa) in the proxy environment and then resolving credentials via Credentials() → fetch → fromEnv.","commonSituations":"Partial .env files, secrets injected individually by an orchestrator where one secret failed to mount, shell rc files exporting only one var, copy-pasting only the access key ID.","solutions":["Export both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY together","Check your .env / deployment secret list so both keys are present and mounted","If you only intended temporary role creds, unset the lone leftover variable so the chain falls through to web identity/container/IMDS"],"exampleFix":"// before\nexport AWS_ACCESS_KEY_ID=AKIA...\n// after\nexport AWS_ACCESS_KEY_ID=AKIA...\nexport AWS_SECRET_ACCESS_KEY=XXXXXXXX\n","handlingStrategy":"validation","validationCode":"id, sec := os.Getenv(\"AWS_ACCESS_KEY_ID\"), os.Getenv(\"AWS_SECRET_ACCESS_KEY\")\nif (id == \"\") != (sec == \"\") {\n\treturn errors.New(\"set both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, or neither\")\n}\n","typeGuard":null,"tryCatchPattern":"creds, err := provider.Credentials(ctx)\nif err != nil {\n\tif strings.Contains(err.Error(), \"must both be set\") {\n\t\t// log which var is missing and abort\n\t}\n\treturn err\n}\n","preventionTips":["Always export the key pair together from the same file/script","Check orchestrator secret mounts include both keys","Unset leftover single variables when switching to role-based creds"],"tags":["aws","credentials","env","partial-config"],"backgroundTag":"missing-env-var","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"}