Skip to main content

The Provided Token Has Expired: Fix AWS CLI ExpiredToken

Fix `An error occurred (ExpiredToken) when calling the ListBuckets operation: The provided token has expired` in the AWS CLI with SSO, assumed roles, and MFA.

8 min readUpdated August 2026

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:

SourceTypical lifetimeRenew with
IAM Identity Center (SSO)Set by your administratoraws sso login
Assumed IAM role1 hour default, up to the role's maximum session durationAssume the role again
MFA session (sts get-session-token)Up to 36 hours for an IAM userNew 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.

Advertisement

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_arn and source_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-identity at 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.

Frequently Asked Questions

Find answers to common questions

You are using temporary credentials — from IAM Identity Center, an assumed role, or an MFA session — and their lifetime has run out. Temporary credentials always expire by design. The fix is to obtain a fresh set, not to change any permission.

Re-authenticate through whichever mechanism issued the credentials. For IAM Identity Center run 'aws sso login --profile my-profile'. For an assumed role, assume it again. For an MFA session, call sts get-session-token with a new one-time code. Then confirm with 'aws sts get-caller-identity'.

ExpiredToken means the credentials were valid and have aged out. InvalidClientTokenId means the access key ID is not recognised at all — typically a deleted key, a typo, or a key from a different account or partition. One is a timing problem, the other is a wrong-value problem.

The operation named in the message is simply whichever API call was attempted first. Any AWS API call with expired credentials produces the same ExpiredToken error with that call's name substituted in, so the operation name is not diagnostic.

It depends on the source. Assumed role sessions default to one hour and can be configured up to the role's maximum session duration, which can be as long as twelve hours. Identity Center session length is set by the administrator. MFA session tokens from get-session-token can run up to 36 hours for an IAM user.

You can raise a role's maximum session duration, but treat that as a deliberate trade-off — a longer window is a longer window for a stolen credential to be used. Automatic renewal is the better answer: configure the profile so the SDK refreshes credentials itself rather than extending their life.

Check the system clock. AWS rejects requests whose signature timestamp is too far from its own, so significant clock drift can make brand-new credentials look expired. Enable NTP time synchronisation on the machine and retry.

The CLI refreshed the credentials in your terminal while the script is holding an older cached copy, or the two are reading different profiles. Long-running processes should create clients through a session that can refresh, rather than capturing key values once at startup.

No. Expiry is a lifecycle event, not an authorization decision, and no policy change affects it. If re-authenticating produces AccessDenied instead, that is a separate permissions issue to diagnose on its own.

For a role session, 'aws sts assume-role' returns an Expiration timestamp in its response. For Identity Center, 'aws sso login' refreshes the cached token under ~/.aws/sso/cache. The quickest live test is 'aws sts get-caller-identity' — it either succeeds or reports the expiry immediately.