If the AWS CLI stops with Unable to locate credentials, it did not fail to reach AWS — it never tried. The CLI walked its entire credential chain, found nothing usable, and gave up before signing a request.
$ aws s3 ls
Unable to locate credentials. You can configure credentials by running "aws configure".
Python users see the same failure from botocore:
botocore.exceptions.NoCredentialsError: Unable to locate credentials
The single most useful thing you can do is stop guessing and ask the CLI what it found:
aws configure list
That prints every setting alongside the source it came from, which tells you precisely which stage of the chain is empty.
Why This Happens
The AWS CLI looks for credentials in a fixed precedence order, and stops at the first stage that yields a complete set. The documented order is:
- Command line options (
--profile,--region) - Environment variables
- Assumed role configuration
- Assumed role with web identity
- IAM Identity Center (
~/.aws/config, populated byaws configure sso) - The credentials file —
~/.aws/credentials, orC:\Users\{USERNAME}\.aws\credentialson Windows - A custom credential process
- The config file —
~/.aws/config - Container credentials (ECS task roles)
- EC2 instance profile credentials, delivered through the instance metadata service
Unable to locate credentials means all ten came up empty. Critically, this is not a permissions problem. No IAM policy, bucket policy, or SCP can cause it, and none can fix it. Adding permissions in response to this error grants real access while leaving the actual cause untouched.
Fix 1: Configure a Profile (No Credentials Set Up Yet)
If aws configure list shows every value as <not set>, nothing is configured. Prefer short-term credentials:
aws configure sso
aws sso login --profile my-profile
IAM Identity Center issues credentials that expire automatically, so a leaked value has a short useful life. If you must use long-term IAM user keys, aws configure writes them to ~/.aws/credentials — but treat that as a fallback, and pair it with a regular key rotation routine.
Fix 2: You Are Using the Wrong Profile
aws configure list shows the active profile. If your credentials live under a named profile, the CLI will not find them unless you say so:
aws s3 ls --profile dev
export AWS_PROFILE=dev # applies to the rest of the shell session
aws configure list-profiles # every profile the CLI can see
Fix 3: The Profile Header Syntax Is Wrong
This trips up nearly everyone once. The two files use different section header formats:
# ~/.aws/credentials — no prefix
[dev]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# ~/.aws/config — "profile" prefix required
[profile dev]
region = us-east-1
output = json
[default] is the one exception: it never takes the profile prefix in either file. A [profile dev] header inside credentials produces a profile the CLI silently ignores.
Fix 4: A Different User or Environment Is Running the Command
Credentials live under $HOME. Anything that changes HOME hides them:
sudoruns as root, whose home is/root, not yours.- cron and systemd start with a minimal environment.
- Docker containers inherit neither your environment nor your home directory.
Point the SDK at the files explicitly when you cannot avoid this:
export AWS_CONFIG_FILE=/home/deploy/.aws/config
export AWS_SHARED_CREDENTIALS_FILE=/home/deploy/.aws/credentials
On ECS or EKS, the better answer is a task role or IRSA — the container credentials stage of the chain then supplies short-term credentials with no files at all.
Fix 5: Environment Variables Are Incomplete
The environment stage requires the whole set, not part of it:
export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
export AWS_SESSION_TOKEN=... # required for temporary credentials only
Setting only the key ID skips the stage entirely. If you assumed a role and omitted AWS_SESSION_TOKEN, you will get a signature error rather than this one — but the missing-variable habit causes both.
Fix 6: EC2 Instance Metadata Is Unreachable
On EC2 the last stage of the chain queries the Instance Metadata Service. It fails when no instance profile is attached, when the metadata endpoint is firewalled, or when the hop limit is 1 and the call originates inside a container (the extra network hop consumes the budget). Raise it to 2 for containerised workloads, and confirm the instance profile is attached in the EC2 console.
Verify the Fix
aws sts get-caller-identity
{
"UserId": "AIDACKCEVSQ6C2EXAMPLE",
"Account": "123456789012",
"Arn": "arn:aws:iam::123456789012:user/dev"
}
This is the cheapest authenticated call in AWS and requires no permissions of its own, so a JSON response proves the CLI both found credentials and signed a request successfully. Once it works, ordinary commands such as those in our S3 CLI cheat sheet will work too.
Prevent It From Recurring
- Use IAM Identity Center rather than long-term keys. Short-term credentials expire on their own, which limits the damage from a leak.
- Never commit credentials. Keep
.aws/and any.envout of version control. - Give hosts roles, not files. EC2 instance profiles, ECS task roles, and IRSA remove credential files from the problem entirely.
- Set
AWS_PROFILEin your shell profile when you work with one account most of the time, so the default is never ambiguous. - Put
aws sts get-caller-identityat the top of deployment scripts. Failing immediately with a clear identity check beats failing halfway through a deployment.