Terraform Error Guide: 'Failed to get existing workspaces' — Fix Backend Credentials, IAM, and Bucket Config
Fix Terraform 'Failed to get existing workspaces': grant s3:ListBucket, fix credentials, correct the bucket and region, and check DynamoDB lock permissions.
- #terraform
- #iac
- #troubleshooting
- #errors
Stuck on this Terraform error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
Failed to get existing workspaces is a backend error. It fires during terraform init, terraform workspace list, or any command that first enumerates the workspaces stored in your remote backend. For the S3 backend, Terraform lists the objects under your state key prefix to discover which workspaces exist; if that ListObjects/ListBucket call is denied, the bucket is wrong, the region is mismatched, or the credentials are missing or expired, Terraform cannot build the workspace list and stops before doing anything else.
The literal message, with the underlying provider error appended, looks like this:
Error: Failed to get existing workspaces: S3 bucket "my-tfstate-bucket" does not exist.
The referenced S3 bucket must have been previously created. If the S3 bucket
was created within the last minute, please wait for a minute or two and try
again.
A permissions variant surfaces the raw AWS API error:
Error: Failed to get existing workspaces: operation error S3: ListObjectsV2,
https response error StatusCode: 403, ... AccessDenied: Access Denied
The text after the colon is the real cause — an AccessDenied, a NoSuchBucket, an InvalidAccessKeyId, or a region redirect. Always read past Failed to get existing workspaces: to the appended provider message.
Symptoms
terraform initfails at the “Initializing the backend…” step withFailed to get existing workspaces.terraform workspace listandterraform planfail the same way.- The trailing error is one of:
AccessDenied(403),NoSuchBucket,InvalidAccessKeyId,ExpiredToken,SignatureDoesNotMatch, or a301/PermanentRedirect(region mismatch). - It works for one engineer but not another — a sign of per-user credentials or IAM policy differences.
- CI suddenly fails after a role, bucket, or region change even though the
.tffiles did not change.
Common Root Causes
- Missing
s3:ListBucket: the IAM principal canGetObject/PutObjectbut lacksListBucketon the bucket ARN — Terraform must list to enumerate workspaces. - Wrong bucket name or it does not exist: a typo in
bucket, a bucket in another account, or a bucket never created. - Region mismatch: the
regionin the backend block does not match the bucket’s actual region, producing aPermanentRedirect/301. - Missing, wrong, or expired credentials: no
AWS_PROFILE/AWS_ACCESS_KEY_ID, an expired STS session token, or the wrong profile assumed. - DynamoDB lock table permissions: with
dynamodb_tableset, the principal needsdynamodb:GetItem/PutItem/DeleteItem(and the table must exist) or related calls fail. - Bucket policy or SCP denial: an explicit
Denyin the bucket policy, an Organizations SCP, or a VPC endpoint policy blockings3:ListBucket. - KMS access missing: if state objects are SSE-KMS encrypted, missing
kms:Decryptcan surface as access errors when Terraform reads existing state.
Diagnostic Workflow
Confirm which identity Terraform is actually using, then test the exact S3 calls it makes:
# Who am I, per the same credential chain Terraform uses?
aws sts get-caller-identity
# Does the bucket exist and what region is it really in?
aws s3api get-bucket-location --bucket my-tfstate-bucket
# Can this identity LIST the state prefix (the call Terraform makes)?
aws s3api list-objects-v2 --bucket my-tfstate-bucket --prefix env: --max-items 5
# If a lock table is configured, can I reach it?
aws dynamodb describe-table --table-name my-tf-locks --region us-east-1
Re-run init with debug logging to see the raw provider error:
TF_LOG=DEBUG terraform init 2>&1 | grep -i 'ListObjects\|AccessDenied\|region\|bucket'
Here is a backend configuration that triggers the error — the region is wrong for the bucket:
terraform {
backend "s3" {
bucket = "my-tfstate-bucket"
key = "prod/terraform.tfstate"
region = "us-west-2" # bucket actually lives in us-east-1
dynamodb_table = "my-tf-locks"
encrypt = true
}
}
And the minimal IAM policy Terraform needs — note ListBucket is on the bucket ARN, object actions on the object ARN:
{
"Version": "2012-10-17",
"Statement": [
{ "Effect": "Allow", "Action": ["s3:ListBucket"],
"Resource": "arn:aws:s3:::my-tfstate-bucket" },
{ "Effect": "Allow", "Action": ["s3:GetObject","s3:PutObject","s3:DeleteObject"],
"Resource": "arn:aws:s3:::my-tfstate-bucket/*" },
{ "Effect": "Allow", "Action": ["dynamodb:GetItem","dynamodb:PutItem","dynamodb:DeleteItem"],
"Resource": "arn:aws:dynamodb:us-east-1:111122223333:table/my-tf-locks" }
]
}
Example Root Cause Analysis
A pipeline that had worked for months began failing every terraform init with:
Error: Failed to get existing workspaces: operation error S3: ListObjectsV2,
https response error StatusCode: 403, ... AccessDenied: Access Denied
aws sts get-caller-identity inside the runner showed the CI role was assuming correctly, and aws s3api get-bucket-location returned the right region — so credentials and region were fine. But aws s3api list-objects-v2 --bucket my-tfstate-bucket returned AccessDenied, while aws s3api get-object on a specific key succeeded. That asymmetry pinned it: the role’s policy granted s3:GetObject/s3:PutObject on arn:aws:s3:::my-tfstate-bucket/* but had no s3:ListBucket statement on arn:aws:s3:::my-tfstate-bucket. A recent policy refactor had dropped the bucket-level statement. Adding the s3:ListBucket statement (on the bucket ARN, not the object ARN) restored terraform init. Root cause: object-level permissions without the bucket-level ListBucket that workspace enumeration requires.
Prevention Best Practices
- Attach the full backend IAM policy —
s3:ListBucketon the bucket ARN plus object actions on/*— and keep it in code so refactors cannot silently drop a statement. - Pin the backend
regionto the bucket’s real region; verify withaws s3api get-bucket-locationwhen creating the bucket. - Add the
dynamodb:GetItem/PutItem/DeleteItempermissions and confirm the lock table exists before enablingdynamodb_table. - If state is SSE-KMS encrypted, grant
kms:Encrypt/kms:Decrypt/kms:GenerateDataKeyon the key. - Validate credentials in CI with
aws sts get-caller-identitybeforeterraform initso identity problems fail fast and clearly. - Use short, explicit assume-role sessions and refresh them before long jobs to avoid
ExpiredTokenmid-run.
Quick Command Reference
# Verify identity, bucket existence, and region
aws sts get-caller-identity
aws s3api get-bucket-location --bucket my-tfstate-bucket
# Reproduce Terraform's list call and lock-table access
aws s3api list-objects-v2 --bucket my-tfstate-bucket --prefix env:
aws dynamodb describe-table --table-name my-tf-locks --region us-east-1
# See the raw backend error
TF_LOG=DEBUG terraform init
# Re-initialise / migrate after fixing backend config
terraform init -reconfigure
terraform workspace list
Conclusion
Failed to get existing workspaces is Terraform failing to enumerate remote state before it can do anything else — and the real reason is always in the text after the colon. Read that appended provider error, then check the four usual suspects: a missing s3:ListBucket permission, wrong or nonexistent bucket, region mismatch, and missing or expired credentials (plus DynamoDB lock permissions when a lock table is set). Reproduce the exact list-objects-v2 call with the AWS CLI to isolate the fault, fix the IAM policy or backend config, and re-run terraform init -reconfigure. Codifying the full backend IAM policy keeps this error from reappearing after future refactors.
Fixed it? Get 500 Terraform & DevOps AI prompts — free
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.
Stuck on this? Start guided troubleshooting
Open an interactive diagnostic session with this error already loaded. Work a step-by-step plan, record what each check returns, land on a root cause, and export a clean incident summary — no account needed to start.
Did this fix your issue?
Solved it a different way?
Share the fix that worked for you — reviewed, then published to help the next engineer.
That looks like it may contain a secret (key, token, password, or connection string). Please remove it — a note with a detected secret can’t be published.
Thanks — that helps. Published notes appear after a quick review.
Trending errors this week
The error guides other engineers are actually reading right now.
- 1mount: wrong fs type, bad option, bad superblock
- 2Docker 'failed to set up container networking': Fix the Bridge and IP Pool
- 3Docker 'failed to create shim task': How to Fix the containerd Runtime Error
- 4Transport endpoint is not connected
- 5modprobe: FATAL: Module not found
- 6mount: wrong fs type, bad option, bad superblock
Get 500 Battle-Tested DevOps AI Prompts — Free
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.