The security token included in the request is expired
Fix ExpiredToken: run aws sts get-caller-identity, then refresh with aws sso login or assume the role again. Covers stale env vars, session length and CI OIDC.
An error occurred (ExpiredToken) when calling the ListBuckets operation: The provided token has expired.
Incident Overview
Fix ExpiredToken: run aws sts get-caller-identity, then refresh with aws sso login or assume the role again. Covers stale env vars, session length and CI OIDC.
easy fix·5 min read
By Vishvesh Patel·DevOps Engineer·Updated 21 September 2026
An error occurred (ExpiredToken) when calling the ListBuckets operation: The provided token has expired. An error occurred (RequestExpired) when calling the DescribeInstances operation: Request has expired. Error: error configuring S3 Backend: no valid credential sources for S3 Backend found. ExpiredTokenException: The security token included in the request is expired
Do this first3 steps
Run these in order. Each one tells you what its output means before you change anything.
• Find out which credential source is being used
The Type column says where each value came from: env, shared-credentials-file, or iam-role. Refreshing the wrong source is the most common reason this error persists after a re-login.
• Refresh the session
For SSO this re-authenticates. For a manually assumed role, re-run the assume-role call. get-caller-identity confirms the new credentials work before you retry the real command.
• Clear stale environment variables that override everything else
Environment variables take precedence over profiles and instance roles, so an expired AWS_SESSION_TOKEN exported hours ago silently shadows a perfectly good profile.
Temporary credentials expire. That is the point of them. ExpiredToken means the STS session behind your call has passed its lifetime.
The last three refresh themselves, so if you are seeing this on an instance, something is bypassing the metadata service.
aws configure list
Which source are you actually using?
Type env is the answer in a large share of cases. Environment variables win over everything, so an AWS_SESSION_TOKEN exported this morning shadows the profile you just refreshed, and re-running aws sso login appears to do nothing.
The precedence order, highest first: environment variables, then the CLI's --profile, then AWS_PROFILE, then the default profile, then container credentials, then the instance metadata service.
aws configure list
Refresh it
Confirm before retrying the real thing:
# IAM Identity Center aws sso login --profile prod # Manually assumed role aws sts assume-role \ --role-arn arn:aws:iam::123456789012:role/deploy \ --role-session-name deploy-$(date +%s)
Stop assuming roles by hand
Configure the profile to do it, and the SDK refreshes automatically when the session expires:
Now aws --profile deploy s3 ls assumes the role when needed and renews it silently. Exporting the output of assume-role into environment variables is what creates a credential that goes stale in your shell with nothing to refresh it.
# ~/.aws/config [profile prod] sso_session = acme sso_account_id = 123456789012 sso_role_name = Developer region = eu-west-1 [profile deploy] source_profile = prod role_arn = arn:aws:iam::123456789012:role/deploy duration_seconds = 3600 [sso-session acme] sso_start_url = https://acme.awsapps.com/start sso_region = eu-west-1 sso_registration_scopes = sso:account:access
Longer sessions
The default assume-role duration is one hour. Raise the role's maximum, then ask for it:
Note role chaining caps at one hour regardless of the maximum. If you assume role A and then assume role B from A, the second session cannot exceed an hour and duration_seconds above that is rejected. This surprises people building nested assume-role setups for a long deployment.
aws iam update-role --role-name deploy --max-session-duration 14400
RequestExpired is a different thing
This is clock skew, not an expired session. Every signed AWS request carries a timestamp, and the service rejects anything more than five minutes out.
Long-suspended laptops and VMs restored from snapshots are the usual sources. Inside a container, the clock comes from the host, so fix it there.
An error occurred (RequestExpired): Request has expired.
In CI, use OIDC
Static access keys in CI secrets are the thing this error should push you away from. Every major CI platform can exchange an OIDC token for a short-lived AWS role session, with no stored credentials at all:
Credentials are minted per job and expire when it ends. Nothing to rotate, nothing to leak.
# GitHub Actions
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/gha-deploy
aws-region: eu-west-1Terraform and the SDK
Terraform reads the same chain. It also caches backend credentials at init time, so a very long apply can outlive the session it started with. For long applies, raise duration_seconds rather than re-running init partway through.
Error: error configuring S3 Backend: no valid credential sources found
A checklist
Frequently Asked Questions
Why does the error persist after I log in again?
Because stale environment variables are overriding your refreshed profile. AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN sit at the top of the credential chain, so an AWS_SESSION_TOKEN exported hours ago shadows whatever aws sso login just wrote. aws configure list shows the source of each value in its Type column. Unset all three and try again; this single step resolves most cases where a re-login appears to have had no effect.
What is the difference between ExpiredToken and RequestExpired?
ExpiredToken means the STS session behind your credentials has passed its lifetime and you need new credentials. RequestExpired means the signed request's timestamp is more than five minutes away from AWS's clock, so it is a time synchronisation problem on your machine rather than a credential one. Check with timedatectl status and restart your NTP service. Laptops resumed from long suspends and VMs restored from snapshots are the usual causes.
How do I get sessions that last longer than an hour?
Raise the role's maximum with aws iam update-role --role-name <name> --max-session-duration 14400, then request it via duration_seconds in the profile or --duration-seconds on the CLI call. The important exception is role chaining: if you assume one role and then assume a second from it, the second session is capped at one hour regardless of the role's maximum, and asking for more is rejected outright. Restructuring to assume the target role directly avoids that cap.
Why do my EC2 instances never hit this?
Because an instance profile's credentials are delivered by the instance metadata service and the SDK refreshes them automatically before they expire. The same is true of ECS task roles and IRSA on EKS. If you do see ExpiredToken on an instance, something is bypassing that mechanism, almost always hardcoded environment variables or a credentials file baked into an AMI. Remove them and let the metadata service supply credentials.
What is the best way to handle AWS credentials in CI?
OIDC federation, which every major CI platform now supports. The platform issues a signed identity token for the job, AWS exchanges it for a short-lived role session, and no long-lived access key is stored anywhere. Credentials are minted per job and expire when it finishes, so there is nothing to rotate and nothing useful to leak. It also gives you per-workflow conditions in the trust policy, so a role can be restricted to one repository and even one branch.
Reference and practice
Learn the underlying concept
Other AWS errors
Free, comprehensive DevOps tutorials and learning roadmaps. Master Docker, Kubernetes, CI/CD, and more.
Get notified about new tutorials and features.
• Roadmaps
• Interview Prep
• Certifications
• Company
© 2026 DevOpsLesson. All rights reserved.
Need to practice this scenario?
Simulate this error condition and practice recovery commands safely in our in-browser terminal.