botocore.exceptions.NoCredentialsError: Unable to locate credentials
Python · updated 2026-11-04
Raised by boto3/botocore on the first API call that needs credentials, after its search order comes up empty: explicit parameters, environment variables, assume-role and web identity settings, ~/.aws/credentials and ~/.aws/config, then the container (ECS) and EC2 instance metadata providers. Creating the client succeeds; the call fails.
Traceback (most recent call last):
File "/app/storage/uploads.py", line 31, in save_upload
s3.put_object(Bucket=BUCKET, Key=key, Body=data)
...
File "/usr/local/lib/python3.12/site-packages/botocore/auth.py", line 418, in add_auth
raise NoCredentialsError()
botocore.exceptions.NoCredentialsError: Unable to locate credentialsQuick fix
On AWS, attach an IAM role to the workload (ECS task role, EKS service account, EC2 instance profile, Lambda execution role); boto3 finds it with no code change. Elsewhere, set AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_DEFAULT_REGION in the process environment. If the error appears only in a container, check that the keys or role reach the container, not just the host.
- Check where the code runs and which credential source should apply there.
- AWS compute: attach a role instead of shipping keys; nothing changes in the code.
- Other hosts: set the AWS_* variables in the deploy environment from a secret manager.
- Don't rely on ~/.aws on servers: the file usually exists only on developer machines.
- Docker on EC2 with IMDSv2: set the metadata hop limit to 2 so the container can read the instance role.
import os
import boto3
# No keys in code: boto3's search order finds the role or the AWS_* env vars.
s3 = boto3.client("s3", region_name=os.environ["AWS_DEFAULT_REGION"])
# Optional startup check, so a bad deploy fails at boot instead of on the first upload
if boto3.Session().get_credentials() is None:
raise RuntimeError("No AWS credentials: attach an IAM role or set AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY")Why it happens
Credentials only in ~/.aws on a laptop
The code works locally through a profile file that the container, CI runner or server does not have.
Environment variables not passed through
The keys are set on the host or in CI but not passed into the container (docker run -e, compose environment, Kubernetes secret).
No role on AWS compute
An ECS task, pod or EC2 instance without a role gives the metadata providers nothing to return.
Metadata endpoint unreachable
A container on EC2 with IMDSv2 and a hop limit of 1, or a network policy blocking 169.254.169.254, hides the instance role.
Other fixes
- Partial credentials (only one of the two keys set) raise PartialCredentialsError instead, naming the missing variable.
- boto3.Session().get_credentials().method tells you which source boto3 used, which helps when the wrong account is picked up.
Same error, other wording
- botocore.exceptions.PartialCredentialsError: Partial credentials found in env, missing: AWS_SECRET_ACCESS_KEY
- botocore.exceptions.NoRegionError: You must specify a region.
- botocore.exceptions.ClientError: An error occurred (ExpiredToken) when calling the PutObject operation: The security token included in the request is expired
Let your coding agent catch the next one
Runheed records this error in production with the stack trace, the breadcrumbs before it and the agent session that shipped the code, and serves it to Claude Code, Cursor or any MCP client. With it connected, you can ask:
> Find the boto3 "Unable to locate credentials" errors in production, tell me where the code runs and which credential source it should use, and fix the deployment (role or environment) without adding keys to the code.
Related errors
- requests.exceptions.ConnectionError: HTTPSConnectionPool(host='api.example.com'...
- PermissionError: [Errno 13] Permission denied: '/app/data/report.csv'
- [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer...
More in Memory, async and the process
- [UnhandledPromiseRejection: This error originated either by throwing inside of an async...Node.js
- FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memoryNode.js
- RangeError: Maximum call stack size exceededNode.js
- RecursionError: maximum recursion depth exceededPython
- RuntimeError: Event loop is closedPython
- RuntimeWarning: coroutine 'fetch_user' was never awaitedPython
- Error: listen EADDRINUSE: address already in use :::3000Node.js
- OSError: [Errno 98] Address already in usePython
- Error: error:0308010C:digital envelope routines::unsupportedNode.js
- MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 error...Node.js
- Error: ENOENT: no such file or directory, open '/app/config.json'Node.js
- Error: EACCES: permission denied, open '/app/data/cache.json'Node.js
- Error: EMFILE: too many open files, open '/app/uploads/0412.jpg'Node.js
- CredentialsProviderError: Could not load credentials from any providersNode.js
- django.core.exceptions.ImproperlyConfigured: The SECRET_KEY setting must not be empty.Python
References: boto3 docs: credentials (search order), botocore source: exceptions, AWS docs: configure instance metadata options (hop limit)