Skip to content
DevOps AI ToolKit
Newsletter
All guides
AWS with AI By James Joyner IV · · 9 min read

AWS Error Guide: 'The security token included in the request is expired' Credential Expiry

Fix the AWS 'security token included in the request is expired' error: refresh STS session tokens, renew assumed-role credentials, and stop ExpiredToken failures.

  • #aws
  • #troubleshooting
  • #errors
  • #sts

Exact Error Message

An error occurred (ExpiredToken) when calling the ListBuckets operation: The security token included in the request is expired

The same condition appears from SDKs and the console as ExpiredTokenException, and from federated sessions as The provided token has expired. The HTTP status is 403 Forbidden, and the code is always ExpiredToken or ExpiredTokenException.

What the Error Means

AWS temporary credentials consist of three parts: an access key ID, a secret access key, and a session token. Unlike long-lived IAM user keys, temporary credentials issued by AWS Security Token Service (STS) — via AssumeRole, GetSessionToken, an EC2 instance profile, an EKS IRSA role, or SSO — carry an explicit expiration timestamp baked into the session at the moment it is minted. Once the clock passes that timestamp, STS rejects any request signed with those credentials and returns ExpiredToken. The expiry is server-enforced, so there is no grace period and no way to nudge a stale token back to life.

This is not a permissions problem. The signature is cryptographically valid and the principal is recognized; the credentials are simply past their lease. That distinction matters because it tells you exactly where to look — the failure lives in the lifecycle of the credentials, not in any IAM policy, trust relationship, or resource ACL. The fix is always to obtain a fresh set of temporary credentials, never to re-grant IAM permissions.

Common Causes

  • Session duration elapsed. AssumeRole defaults to a one-hour session. A long-running job that cached credentials at start fails an hour later, often without any code change to explain it. This is the single most common cause and the reason builds break “around the 60-minute mark.”
  • GetSessionToken window closed. MFA-backed session tokens default to 12 hours and cap at 36 hours; long pipelines and overnight batch jobs routinely outlive them. Because the window is so long, the failure often surprises engineers who assumed the credentials were effectively permanent.
  • Stale credentials cached in environment variables. A shell exported AWS_SESSION_TOKEN hours ago and never refreshed it, so every subsequent command keeps re-using a dead token. Exported variables persist for the life of the shell, a frequent trap in interactive debugging.
  • A paused or suspended process. A laptop that slept, or a container paused for hours, resumes with expired credentials because wall-clock time advanced while the process was frozen.
  • SSO/identity-center token expiry. aws sso login sessions expire and the cached token under ~/.aws/sso/cache is no longer valid, so every profile that relies on that SSO session begins failing at once.
  • Cross-account role chaining. Chained AssumeRole calls are capped at one hour regardless of the role’s MaxSessionDuration, so a job that hops through an intermediate role cannot extend its lease past 60 minutes.

How to Reproduce the Error

Assume a role with a short session and let it lapse:

# Get credentials with a 900-second (15-minute) lifetime
aws sts assume-role \
  --role-arn arn:aws:iam::123456789012:role/build-bot-role \
  --role-session-name repro --duration-seconds 900 \
  --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken,Expiration]' \
  --output text

Export those three values, wait past the Expiration timestamp, then make any call:

aws s3api list-buckets
An error occurred (ExpiredToken) when calling the ListBuckets operation: The security token included in the request is expired

Diagnostic Commands

Confirm the calling identity and whether it is a temporary session:

aws sts get-caller-identity

An ARN containing assumed-role/ or federated-user/ confirms temporary credentials that expire. Inspect the cached SSO/role expiration:

aws configure list

Check what session token (if any) is in the environment without printing its value:

aws configure list | grep -E 'token|secret_key'

For SSO sessions, list the cached credentials’ expiry:

aws sts get-caller-identity --query Arn --output text

Confirm the local clock is correct (a skewed clock can masquerade as token problems):

aws ec2 describe-regions --query 'Regions[0].RegionName' --output text && date -u

Step-by-Step Resolution

  1. Identify how the credentials were issued. Run aws sts get-caller-identity. An assumed-role ARN means STS; an instance-profile ARN means the EC2/EKS metadata service. This single step usually tells you which of the remaining steps applies, so know the credential source before changing any configuration.

  2. Re-issue the credentials. For an explicitly assumed role, call AssumeRole again and re-export the new triple. Capture all three values — access key ID, secret access key, and session token — because exporting only two produces a different, equally confusing failure:

    aws sts assume-role \
      --role-arn arn:aws:iam::123456789012:role/build-bot-role \
      --role-session-name refreshed \
      --query 'Credentials' --output json
  3. Refresh SSO sessions with aws sso login --profile <profile>, then retry. This re-opens the browser flow, mints a new cached token, and revalidates every dependent profile in one step.

  4. Stop hand-managing credentials. Remove stale AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN exports and let the SDK’s credential provider chain refresh automatically using a named profile or instance role. Automated providers re-read the source on every call, so they sidestep the entire class of “I exported a token hours ago” failures.

  5. Extend the session window where appropriate. Raise --duration-seconds (up to the role’s MaxSessionDuration, max 12 hours for direct assumption) so long jobs do not outlive their lease. Treat this as a complement to in-loop refresh — a job longer than any allowed session still needs to re-fetch credentials as it goes.

  6. Verify with aws sts get-caller-identity, then re-run the failed command. A clean identity response proves the new session is live before you spend more time re-running an expensive job.

Prevention and Best Practices

  • Prefer the SDK default credential provider chain or instance/IRSA roles, which refresh automatically, over manually exported session tokens. The provider chain re-reads the source on every call, so it eliminates the most common cause of ExpiredToken entirely.
  • Set the role’s MaxSessionDuration to match the longest job, then request that duration explicitly — but no longer than needed, since over-provisioning leaves valid credentials in flight longer than necessary.
  • For pipelines that run over an hour, re-fetch credentials inside the loop rather than once at startup. A small refresh helper that re-assumes before expiry costs little and prevents the classic one-hour failure outright.
  • Treat aws sso login as a routine pre-flight step in scripts and refresh proactively before expiry, so a session that lapsed overnight never becomes a mid-run surprise.
  • Monitor for ExpiredToken in CloudTrail and alert; a spike usually means a process is caching credentials too long, and catching the pattern early saves you from debugging the same failure repeatedly.
  • Keep host clocks synchronized with NTP so genuine expiry is not confused with clock skew, which produces a similar 403 from a different root cause.
  • InvalidClientTokenId — the access key ID itself is unknown or deactivated, not merely expired.
  • The security token included in the request is invalid — malformed or mismatched session token, often from copying only two of the three values.
  • SignatureDoesNotMatch — the secret key or signing process is wrong, a different failure than expiry.
  • AccessDenied — valid, unexpired credentials that lack the required permission.

Frequently Asked Questions

Does re-running my IAM policy fix this? No. Permissions are irrelevant to ExpiredToken. The principal is already recognized and the signature already validates, so editing the policy changes nothing about the lease. You must obtain fresh temporary credentials.

Why do my EC2 instance-role credentials sometimes expire? The instance metadata service rotates them automatically well before they lapse; you only see ExpiredToken if your code cached the values once instead of re-reading the metadata endpoint (or letting the SDK do so) each time it signs a request. The fix is almost always to stop caching and lean on the provider chain.

How long do assumed-role credentials last? One hour by default, up to the role’s MaxSessionDuration (max 12 hours) when assumed directly, and capped at one hour when role chaining. Knowing which applies tells you the maximum lease you can request when sizing long jobs.

Can I extend an existing session? No. STS sessions are immutable once issued; there is no renew API for a token already in hand. You call AssumeRole again to mint a brand-new session, then swap the new triple in place of the old one.

Why does my pipeline fail exactly one hour in? It almost certainly cached credentials at startup with the default one-hour lifetime, then kept signing with them as the clock ran down. Refresh credentials inside long-running steps, or request a longer explicit duration, so the lease outlives the work. Retrying the same request without refreshing only repeats the failure. See the AWS guides for credential-chain patterns.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.