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 credentials

Quick 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.

  1. Check where the code runs and which credential source should apply there.
  2. AWS compute: attach a role instead of shipping keys; nothing changes in the code.
  3. Other hosts: set the AWS_* variables in the deploy environment from a secret manager.
  4. Don't rely on ~/.aws on servers: the file usually exists only on developer machines.
  5. Docker on EC2 with IMDSv2: set the metadata hop limit to 2 so the container can read the instance role.
python
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.

Plans from $29/moSee what your agent readsSee the setupCancel any time from the billing portal

Related errors

More in Memory, async and the process

References: boto3 docs: credentials (search order), botocore source: exceptions, AWS docs: configure instance metadata options (hop limit)