Fix Terraform AWS ExpiredToken Session Errors

Tested on Ubuntu 26.04 LTS (Resolute Raccoon)
Package terraform 1.16.1
hashicorp/aws 6.63.0
awscli 2.36.38
Applies to Any host with Terraform and the AWS provider; examples use ap-south-1
Privilege AWS identity that can call sts:GetCallerIdentity; normal user on the workstation
Scope Diagnose and fix ExpiredToken and related STS session failures from the HashiCorp AWS provider or S3 remote state backend during terraform plan or apply — stale AWS_SESSION_TOKEN environment variables, expired IAM Identity Center (SSO) sessions, aws configure export-credentials exports, and temporary credentials from sts:GetSessionToken or sts:AssumeRole. Does not cover missing credentials (No valid credential sources found), IAM permission denials (UnauthorizedOperation), or VPC destroy dependency errors.
Related guides Install AWS CLI on Ubuntu
Short on time?Beta Get the gist of this 9-minute read in seconds, then ask follow-up questions

Terraform reached AWS, but the provider rejected your session before it could read any resource:

text
Error: Retrieving AWS account details: validating provider credentials: retrieving caller identity from STS: operation error STS: GetCallerIdentity, https response error StatusCode: 403, RequestID: …, api error ExpiredToken: The security token included in the request is expired

The AWS CLI prints the same root cause in shorter form:

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

That is different from missing credentials and different from IAM denying an API call. AWS recognized the credential shape but the STS session is past its expiration time.


What Terraform ExpiredToken means

  • Your access key id, secret key, and session token were valid at some point, but the session token lifetime has ended
  • Terraform fails during provider configuration or the first API call — often on sts:GetCallerIdentity before any resource is planned
  • The fix is to refresh or remove the temporary credentials, not to edit .tf files
NOTE
If Terraform cannot find credentials at all, you get No valid credential sources found — see Fix Terraform AWS credentials error. If AWS authenticates you but denies a specific API action, see Fix Terraform AWS UnauthorizedOperation.

Related errors you may see instead of ExpiredToken:

Error What it usually means
ExpiredToken STS session token is past its expiration timestamp
InvalidClientTokenId Token is malformed, revoked, or a leftover AWS_SESSION_TOKEN does not match the access key
SignatureDoesNotMatch … Signature expired Workstation clock is far behind AWS — sync NTP before refreshing credentials
refresh cached SSO token failed … SSO session has expired IAM Identity Center portal session expired — run aws sso login again

Find which AWS credentials Terraform is using

The HashiCorp AWS provider can obtain credentials from provider configuration parameters, environment variables, shared credentials files, shared configuration files, container credentials, or instance profile credentials — in that order of precedence.

Stale AWS_* environment variables are especially important to check when the provider does not explicitly select a profile, because environment credentials take precedence over credentials that would otherwise come from shared AWS files.

Check the active identity in the same shell that runs Terraform:

bash
aws sts get-caller-identity

If that fails with ExpiredToken or InvalidClientTokenId, Terraform will fail too — fix the shell first.

List credential-related environment variables:

bash
env | grep '^AWS_'

What to look for:

  • AWS_ACCESS_KEY_ID starting with ASIA — temporary credentials that include a session token
  • AWS_SESSION_TOKEN set from an old eval "$(aws configure export-credentials …)" or CI job
  • AWS_PROFILE pointing at an SSO profile whose browser login expired

When you use a named profile in the provider block, the provider uses that profile rather than environment credentials. Confirm the profile name matches what you logged into:

hcl
provider "aws" {
  region  = "ap-south-1"
  profile = "corp-sso"
}

Fix Terraform ExpiredToken

Most fixes follow the same pattern: identify where the credentials came from, renew the source session if needed, then retry Terraform in the same shell.

Stale AWS_SESSION_TOKEN or exported STS credentials

Many teams export credentials once and reuse the shell all day. Temporary keys expire when the underlying STS session ends.

Symptom: aws sts get-caller-identity worked earlier in the same terminal; both CLI and Terraform now return ExpiredToken or InvalidClientTokenId.

aws configure export-credentials exports whatever credentials the CLI currently resolves for a profile. It does not renew an already-expired STS session by itself. Renew the source credentials first, then export:

bash
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN

Replace corp-sso with the profile that supplies your temporary credentials:

bash
aws sso login --profile corp-sso

Export the refreshed session into environment variables Terraform can read:

bash
eval "$(aws configure export-credentials --profile corp-sso --format env)"

Confirm the new identity:

bash
aws sts get-caller-identity

Run plan again in that same shell:

bash
terraform plan

Without --profile, export-credentials uses the default profile. For scripts and CI, chain login, export, and Terraform in one step:

bash
aws sso login --profile corp-sso && eval "$(aws configure export-credentials --profile corp-sso --format env)" && terraform plan

If the underlying profile itself still holds expired temporary credentials, export-credentials cannot renew them — fix the source (SSO login, aws login, or a new sts:AssumeRole call) before exporting.

The same export pattern appears in cloud guides such as deploy EKS with Terraform — refresh credentials in every shell before terraform init, plan, or apply.

IAM Identity Center / AWS SSO session expired

Teams using IAM Identity Center should use the recommended sso-session configuration in ~/.aws/config so AWS SDKs can refresh access tokens while the portal session remains valid:

ini
[profile corp-sso]
sso_session = corp-sso
sso_account_id = 123456789012
sso_role_name = AdministratorAccess
region = ap-south-1

[sso-session corp-sso]
sso_start_url = https://my-org.awsapps.com/start
sso_region = ap-south-1
sso_registration_scopes = sso:account:access

When the IAM Identity Center portal session itself expires, the provider may report:

text
refresh cached SSO token failed, failed to refresh cached credentials, the SSO session has expired or is invalid

Log in again:

bash
aws sso login --profile corp-sso

Confirm identity with that profile:

bash
aws sts get-caller-identity --profile corp-sso

Point Terraform at the profile:

hcl
provider "aws" {
  region  = "ap-south-1"
  profile = "corp-sso"
}

With the sso-session format above, the AWS SDK can refresh credentials automatically while the portal session is still valid. Run aws sso login --profile corp-sso again after the portal session itself expires.

Alternatively, export standard environment variables after login:

bash
eval "$(aws configure export-credentials --profile corp-sso --format env)"
terraform plan

Stale exported temporary credentials override your long-term profile

You may have valid long-term credentials in ~/.aws/credentials but still have an older temporary credential set exported in the shell:

bash
env | grep '^AWS_'

Example output:

output
AWS_ACCESS_KEY_ID=ASIAEXAMPLE
AWS_SECRET_ACCESS_KEY=example-secret
AWS_SESSION_TOKEN=example-session-token

Because environment credentials take precedence over shared credential files when no provider profile is explicitly selected, Terraform uses this expired temporary credential set instead of the long-term credentials in ~/.aws/credentials.

Clear the stale export and verify identity:

bash
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
aws sts get-caller-identity

When identity checks succeed, re-run Terraform:

bash
terraform plan

Credentials expired during terraform apply

Temporary credential lifetime depends on how the credentials were issued. AssumeRole sessions can range from 15 minutes up to the role's configured maximum of 12 hours, while other STS operations have different limits.

A long terraform apply on a large stack can outlive the session if credentials were exported at the start of the day.

Prevention:

  • Refresh credentials immediately before terraform apply, not only before plan
  • Prefer a provider profile backed by refreshable IAM Identity Center credentials over static AWS_* exports when your workflow allows
  • In CI, fetch credentials in the same job step that runs Terraform — do not reuse artifacts from an earlier stage

If terraform apply fails after some resources were created, refresh the credentials and run terraform plan again. Terraform refreshes the current resource state and shows what remains to be changed; normally you do not need to destroy resources that were successfully created.


Is ExpiredToken coming from the provider or S3 backend?

ExpiredToken can appear while Terraform talks to AWS in two different places.

Provider credentials — errors mention Retrieving AWS account details, GetCallerIdentity, or a specific AWS resource API (for example ec2:DescribeVpcs). Troubleshoot provider credentials using the steps above.

S3 backend credentials — errors mention the S3 backend, state persistence, PutObject, GetObject, or acquiring or releasing a state lock. The credentials used by the remote backend have expired even though your provider block might still be valid.

When the backend is the source:

  • Refresh AWS credentials the same way — aws sso login, then export-credentials if you rely on environment variables
  • Retry the Terraform operation (terraform init, plan, or apply)
  • Do not run terraform force-unlock or edit state solely because credentials expired — renew credentials first

Troubleshooting

Symptom Likely cause Fix
ExpiredToken on plan or apply Exported AWS_SESSION_TOKEN past expiration Renew source session (aws sso login), then eval "$(aws configure export-credentials --profile <name> --format env)"
ExpiredToken although valid credentials exist in ~/.aws/credentials Stale temporary credential trio exported in shell unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
aws sts works, Terraform fails Different shell, profile, or env vars Run get-caller-identity in the Terraform shell; align AWS_PROFILE and provider profile
SSO profile in provider block fails IAM Identity Center portal session expired aws sso login --profile <name>
refresh cached SSO token failed Same as expired portal session Re-login; export credentials if needed
Worked at plan, failed at apply Session expired between commands Refresh credentials immediately before apply
SignatureDoesNotMatch / Signature expired VM clock behind AWS Check system time with timedatectl status and restore time synchronization
Error mentions S3 backend or state lock Backend credentials expired Refresh credentials; retry — do not force-unlock state for auth expiry alone
CI fails on schedule only Injected credentials from prior pipeline stage Generate credentials in the same step as terraform

For broader Terraform failure stages, see Terraform troubleshooting.


Summary

ExpiredToken during terraform plan or apply means Terraform signed AWS requests with a session token that is no longer valid. That is a credential lifetime problem, not a syntax error in your Terraform configuration.

Start in the same shell that runs Terraform: run aws sts get-caller-identity and inspect AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN. When exports are stale, renew the underlying session (aws sso login or equivalent), then run eval "$(aws configure export-credentials --profile <name> --format env)". When a stale temporary credential trio in the shell overrides long-term keys in ~/.aws/credentials, unset all three AWS_* variables.

Use the recommended sso-session profile format so the AWS SDK can refresh credentials while the IAM Identity Center portal session remains valid. For long applies, refresh credentials right before terraform apply. When the error mentions the S3 backend instead of a provider API, renew the same AWS credentials and retry — do not modify state locks solely because auth expired.


References


Frequently Asked Questions

Is ExpiredToken the same as No valid credential sources found?

No. No valid credential sources found means Terraform could not find any credentials. ExpiredToken means credentials were present but the session token lifetime ended.

Why does aws sts work in one terminal but Terraform fails in another?

Each shell has its own environment. SSO logins and export-credentials do not propagate automatically. Export or log in in the shell that runs Terraform.

Should I put access keys in the provider block to avoid expiration?

No. Long-lived keys in code expire rotation policies and widen blast radius. Refresh temporary sessions or use SSO profiles instead.

Does terraform apply automatically refresh SSO tokens?

When you use a provider profile backed by IAM Identity Center with an sso-session block, the AWS SDK can refresh access tokens while the portal session remains valid. Static AWS_* environment variables do not auto-refresh — re-export them.

Plan succeeded yesterday; today both plan and apply fail with ExpiredToken

Your exported session expired overnight. Unset AWS_* variables, log in again, export fresh credentials, and retry.

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with 15+ years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)