Recovering from a Stuck Terraform State Lock

A failed terraform apply left our DynamoDB state lock held by a dead process. The force-unlock command returned 'LocalState not locked' until we figured out what we were doing wrong.

A terraform apply had crashed mid-run — likely from a network interruption or a session timeout. When we tried to run the next apply, we hit:

Error: Error acquiring the state lock

Error message: ConditionalCheckFailedException: The conditional request failed
Lock Info:
  ID:        e0fa1cee-a078-f89e-d98e-95db8c666fcf
  Path:      staging/terraform.tfstate
  Operation: OperationTypeApply
  Who:       runner@ci-host
  Version:   1.14.7
  Created:   2026-04-02 23:11:08.349447 UTC
  Info:

The lock was held by a process that no longer existed.

First Attempt: terraform force-unlock from the Wrong Place

Our first instinct was to run terraform force-unlock from wherever we happened to be:

cd terraform
terraform force-unlock e0fa1cee-a078-f89e-d98e-95db8c666fcf

Result:

Failed to unlock state: LocalState not locked

This was confusing. “LocalState not locked” — but the error above clearly said the state locked in DynamoDB. What were we missing?

The Problem: Working Directory Matters

terraform force-unlock operates on the current working directory’s backend configuration. When we ran it from terraform/ (the modules root), Terraform had no backend configured there. It was trying to unlock a local state file that didn’t exist.

The staging environment backend is configured in terraform/environments/staging/main.tf:

backend "s3" {
  bucket         = "mypie-terraform-state-staging"
  key            = "staging/terraform.tfstate"
  region         = "eu-central-1"
  dynamodb_table = "mypie-terraform-locks"
}

The fix was to cd into the correct directory first. But there was a second obstacle.

Second Problem: terraform init Required

After changing to the staging directory, running force-unlock still failed:

Error: Module not installed

  This module is not yet installed. Run "terraform init" to install all modules
  required by this configuration.

The staging directory needed terraform init before any Terraform operation could run, including force-unlock. Modules weren’t downloaded yet:

cd terraform/environments/staging
terraform init
Initializing modules...
- eks in ../../modules/eks
- networking in ../../modules/networking
...

Terraform has been successfully initialized!

The Actual Fix

With the correct directory and an initialized workspace:

cd terraform/environments/staging
terraform force-unlock e0fa1cee-a078-f89e-d98e-95db8c666fcf

Terraform prompted for confirmation:

Do you really want to force-unlock?
  Terraform will remove the lock on the remote state.
  This will allow local Terraform commands to modify this state, even though it
  may be still in use. Only 'yes' will be accepted to confirm.

  Enter a value: yes

Terraform state has been successfully unlocked!

Then the next terraform apply ran cleanly.

Verifying the Lock in DynamoDB (Optional)

If you want to inspect the lock directly before removing it, the DynamoDB item looks like this:

aws dynamodb get-item \
  --table-name mypie-terraform-locks \
  --key '{"LockID": {"S": "staging/terraform.tfstate"}}' \
  --region eu-central-1

You can also delete it directly if force-unlock is unavailable:

aws dynamodb delete-item \
  --table-name mypie-terraform-locks \
  --key '{"LockID": {"S": "staging/terraform.tfstate"}}' \
  --region eu-central-1

Warning: Only do this if you are certain no other Terraform process is actually running. Deleting an active lock can cause state corruption if two applies run concurrently.

Checklist for Stuck Locks

  1. cd into the environment directory that owns the stuck state (not a parent or sibling)
  2. Run terraform init if the directory has not been initialized in the current shell
  3. Run terraform force-unlock <lock-id> — the lock ID is printed in the error message
  4. Verify with a terraform plan before the next apply