If the AWS CLI stops with The provided token has expired, nothing is misconfigured. Your temporary credentials simply reached the end of their lifetime.
$ aws s3 ls
An error occurred (ExpiredToken) when calling the ListBuckets operation: The provided token has expired.
You may see closely related wording depending on the service and credential type:
An error occurred (RequestExpired) when calling the DescribeInstances operation:
Request has expired.
The security token included in the request is expired
All of these mean the same thing: re-authenticate. No IAM policy change is involved, and none will help.
Why This Happens
Every temporary credential AWS issues carries a hard expiry timestamp. That is the security property that makes short-term credentials preferable to long-term access keys — a stolen value stops working on its own. Three sources issue them:
| Source | Typical lifetime | Renew with |
|---|---|---|
| IAM Identity Center (SSO) | Set by your administrator | aws sso login |
| Assumed IAM role | 1 hour default, up to the role's maximum session duration | Assume the role again |
MFA session (sts get-session-token) | Up to 36 hours for an IAM user | New one-time code |
Long-term IAM user access keys do not expire, so if you see ExpiredToken you are — by definition — using temporary credentials somewhere, even if you did not set them up yourself.
The operation named in the message is just whichever call happened first. ListBuckets, GetCallerIdentity, DescribeInstances — the name tells you nothing about the cause.
Fix 1: IAM Identity Center (Most Common)
aws sso login --profile my-profile
aws sts get-caller-identity --profile my-profile
If the browser flow completes but the CLI still reports expiry, clear the cached token and log in again:
rm -rf ~/.aws/sso/cache
aws sso login --profile my-profile
Fix 2: An Assumed Role Session Ended
Assume the role again:
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/DeploymentRole \
--role-session-name deploy-session
The response includes an Expiration field — useful for confirming how long you actually have.
Better still, stop copying credentials by hand. Configure the role in ~/.aws/config and let the CLI assume and refresh it for you:
[profile deploy]
role_arn = arn:aws:iam::123456789012:role/DeploymentRole
source_profile = default
region = us-east-1
With role_arn and source_profile set, the SDK renews the session automatically when it expires — which removes this error from your day entirely.
Fix 3: An MFA Session Token Ran Out
aws sts get-session-token \
--serial-number arn:aws:iam::123456789012:mfa/your-user \
--token-code 123456
Export all three returned values. A missing AWS_SESSION_TOKEN produces a signature error rather than an expiry error, but the two get confused constantly:
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...
If you have not yet enabled MFA on the account, our MFA setup guide covers it.
Fix 4: Stale Environment Variables
Exported credentials are frozen at the moment you exported them and are never refreshed. Worse, environment variables sit near the top of the credential chain, so a stale set overrides the perfectly good profile you just logged into. If aws sso login succeeds and the very next command still fails, this is almost certainly why:
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
aws sts get-caller-identity
Fix 5: Docker and ECR Login Tokens
Amazon ECR authentication tokens are separate from your AWS credentials and are valid for 12 hours. A build host that has been up for a day will push successfully in the morning and fail in the evening with an authorization failure, even though aws sts get-caller-identity still works perfectly. Re-authenticate the Docker client:
aws ecr get-login-password --region us-east-1 |
docker login --username AWS --password-stdin \
123456789012.dkr.ecr.us-east-1.amazonaws.com
Put that step inside the pipeline rather than running it by hand on the runner, so every build starts with a fresh token instead of inheriting whatever the previous build left behind.
Fix 6: The System Clock Is Wrong
AWS validates request signatures against its own clock and rejects requests whose timestamps drift too far. Significant local clock skew can make brand-new credentials look expired:
date -u # compare against real UTC
timedatectl status # Linux: confirm NTP is active
sudo timedatectl set-ntp true
This is a common cause on virtual machines resumed from suspend and on long-lived containers.
Verify the Fix
aws sts get-caller-identity
A JSON response with UserId, Account, and Arn confirms valid credentials. If you now get AccessDenied instead, that is a different problem — authentication succeeded and authorization failed — and should be diagnosed as a permissions issue rather than by extending session lifetimes.
Prevent It From Recurring
- Let the SDK refresh for you. A profile with
role_arnandsource_profile, or an Identity Center profile, renews automatically. Manually exported credentials never do. - Do not reflexively extend session duration. Raising a role's maximum session duration widens the window in which a stolen credential is useful. Refresh more often instead of expiring less often.
- Give compute an identity instead of credentials. EC2 instance profiles, ECS task roles, and IRSA rotate underneath your workload with no expiry handling on your part.
- Fail fast in automation. Put
aws sts get-caller-identityat the start of deployment scripts so an expired session stops the run immediately rather than halfway through. - Keep clocks synchronised on every machine that calls AWS.