Skip to main content
Fix "Error Acquiring the State Lock" in Terraform

Fix "Error Acquiring the State Lock" in Terraform

KU
• 8 min read•Updated •20 views

A stuck state lock stops every apply cold, and the fix is one command away once you confirm nobody else is actually running Terraform.

Key takeaways

  • Terraform's "Error acquiring the state lock" means another operation holds the state lock, or a crashed or killed run left a stale one.
  • Read the Who and Created fields in Lock Info, confirm no apply is live, then run terraform force-unlock with the exact lock ID.
  • Prevent repeats with CI concurrency groups and -lock-timeout.

"Error acquiring the state lock" means another Terraform operation owns the state lock, or a run that crashed left a stale one behind. Read the Who and Created fields in the error, confirm nothing is running, then release it with terraform force-unlock <LOCK_ID>. Nothing in your infrastructure is broken; Terraform is refusing to proceed on purpose.

Verified against

Terraform 1.17 (1.17.1, released 2026-10-28), against the developer.hashicorp.com state locking, force-unlock and S3 backend pages. Checked October 2026.

What state locking is and why it exists

Terraform stores the mapping between your configuration and real resources in a state file. Any operation that could write state (apply, destroy, import, state mv) first takes a lock on the backend so only one writer runs at a time. The docs are blunt: if state locking fails, Terraform does not continue.

Without locking, two concurrent applies can interleave their writes and leave state describing resources that do not match reality, one of the hardest failure modes to recover from. The lock is taken at the start of the operation and released when it finishes cleanly. Most of the time you see this error, a previous run died before the release.

What the error looks like

The message hands you everything you need. This one is from the S3 backend with a DynamoDB lock table:

code
$ terraform apply
Acquiring state lock. This may take a few moments...
╷
│ Error: Error acquiring the state lock
│
│ Error message: operation error DynamoDB: PutItem, ...
│ ConditionalCheckFailedException: The conditional request failed
│ Lock Info:
│   ID:        d8f3c1a2-9b7e-4f6a-8c21-2a5f0e9d7b41
│   Path:      my-tf-state/prod/terraform.tfstate
│   Operation: OperationTypeApply
│   Who:       kiril@build-agent-07
│   Version:   1.17.1
│   Created:   2026-10-29 14:02:11.884213 +0000 UTC
│   Info:
╵

Read the Lock Info block first. Who is the user and machine holding the lock, Created is its age, Operation is what it was doing. If Who is your own CI runner from twenty minutes ago and no job is active, the lock is stale. If Who is a colleague and the timestamp is thirty seconds old, someone is applying right now and you should wait.

The three causes

A previous run crashed or was interrupted. Someone hit Ctrl-C mid-apply, a laptop slept, or a CI job hit its timeout and got killed. Terraform never reached the release step.

A genuine concurrent run. Two pipelines fired off the same branch, or a teammate is applying by hand while CI does the same. This is what the lock exists to catch. The fix is patience, not force.

A network blip to the backend. The lock was written, then the connection dropped before the release completed. The lock record persists though nothing is running.

The safe fix

The order of operations matters more than the command.

  1. Check whether anyone is running Terraform: your CI dashboard for in-flight jobs, your team channel, and the Who and Created fields against reality.
  2. Only once you are confident nothing is live, release the lock with the exact ID from the error:
code
$ terraform force-unlock d8f3c1a2-9b7e-4f6a-8c21-2a5f0e9d7b41
Do you really want to force-unlock?
  ...
  Enter a value: yes

Terraform state has been successfully unlocked!

Run it from the same configuration directory, because the command removes the lock on the state for the current configuration. -force skips the prompt. Per the docs, force-unlock does not modify your infrastructure, and the lock ID acts as a nonce so you cannot release the wrong lock by accident.

The danger of force-unlock

HashiCorp's locking page says to "be very careful with this command" and to use it only "to unlock your own lock in the situation where automatic unlocking failed." Unlock a lock that a live apply owns and you have invited a second writer into the state. Recovering can mean hand-editing JSON, re-importing resources, or restoring a backend object version, all under pressure. Never wire force-unlock into an automatic retry or an unattended pipeline step.

The error strings by backend, and the last-resort cleanup

BackendWhat the lock error saysWhere the lock lives
S3 + DynamoDB (dynamodb_table)ConditionalCheckFailedException: The conditional request failedAn item in the table whose LockID is the state Path
S3 native (use_lockfile = true)An S3 PreconditionFailed error on the lock objectA <key>.tflock object next to the state
GCSAn Error 412 precondition failure on a .tflock objectA lock object in the state bucket
azurermstate blob is already lockedA lease on the state blob
LocalLocal state cannot be unlocked by another processThe local process; stop it instead

When force-unlock itself cannot reach the backend, go to the store directly, and only after confirming no run is active. For DynamoDB, the lock item's LockID is the Path from Lock Info. Do not touch the separate ...-md5 item; that holds the state checksum, not the lock:

code
$ aws dynamodb get-item --table-name terraform-locks \
    --key '{"LockID": {"S": "my-tf-state/prod/terraform.tfstate"}}'
$ aws dynamodb delete-item --table-name terraform-locks \
    --key '{"LockID": {"S": "my-tf-state/prod/terraform.tfstate"}}'

For S3-native locking, the lock is an ordinary object:

code
$ aws s3 ls s3://my-tf-state/prod/
$ aws s3 rm s3://my-tf-state/prod/terraform.tfstate.tflock

On HCP Terraform, locking is per workspace and there is no table to edit. Unlock from the workspace UI or with force-unlock, and check who locked it first: humans can lock workspaces deliberately, and it looks the same.

Prevention: move off DynamoDB, serialize CI, wait instead of failing

As of October 2026 the S3 backend docs mark DynamoDB-based locking as deprecated and say it "will be removed in a future minor version." Turn on S3-native locking; both arguments can be set together while you migrate:

HCL
# backend.tf
terraform {
  backend "s3" {
    bucket       = "my-tf-state"
    key          = "prod/terraform.tfstate"
    region       = "eu-west-1"
    use_lockfile = true
  }
}

Then run terraform init -reconfigure. The role needs s3:GetObject, s3:PutObject and s3:DeleteObject on the .tflock key.

Serialize CI so only one job per state runs at a time. In GitHub Actions, a concurrency group queues runs instead of colliding (GitLab's equivalent is resource_group):

YAML
# .github/workflows/terraform.yml
concurrency:
  group: terraform-prod
  cancel-in-progress: false

Keep cancel-in-progress false: cancelling a running apply is exactly how locks get orphaned. Finally, let Terraform wait out short overlaps instead of failing instantly:

code
$ terraform apply -lock-timeout=120s

For the broader catalog of plan, provider, and module failures, keep the Terraform error troubleshooting guide handy. If you are restructuring backends anyway, migrating Terraform state covers the move.

Where this breaks

Everything here assumes a backend that supports locking; not all do, and each backend's page says whether it does. If yours does not, the lock error never appears and concurrent runs simply corrupt state, so serializing CI is your only protection. And force-unlock cannot fix a state that two writers already mangled; that is a restore from a versioned backup, not an unlock.

Questions people ask

What does "Acquiring state lock. This may take a few moments" mean?

It is the normal message Terraform prints before any command that can write state, and it usually clears in a second. If it hangs and then fails with "Error acquiring the state lock", another process holds the lock or a dead run left it behind. With -lock-timeout=120s set, Terraform keeps retrying for that long before giving up instead of failing immediately.

Is it safe to run terraform force-unlock?

Only after you confirm no operation is live. HashiCorp's locking docs say force unlock "should only be used to unlock your own lock in the situation where automatic unlocking failed." Unlocking someone else's active run lets two writers touch the same state, which is exactly the corruption locking prevents. The command needs the exact lock ID, and it never changes real infrastructure.

Can I just use -lock=false?

You can for most commands, but HashiCorp's docs say they do not recommend it, and it skips the safety check rather than clearing the stale lock. It can be tolerable for a read-only plan on a state nobody else is touching. For apply, release the stale lock properly with terraform force-unlock and the ID from the error.

The call we'd make

Treat a lock error as a question, not an emergency. Read the Lock Info, confirm from CI and your team that nothing is running, then force-unlock with the exact ID. Reach for backend-level deletion only when force-unlock cannot talk to the backend. Then spend ten minutes switching to use_lockfile, adding a concurrency group, and setting -lock-timeout, so the same interruption does not page you next week.

Sources

Explore topics:TerraformDevOps
React
Share this post
Cite this article
Plain text

Kiril Urbonas (2026). Fix "Error Acquiring the State Lock" in Terraform. DevOpsNess. Updated 2026-10-31. https://www.devopsness.com/blog/fix-terraform-state-lock

Markdown

[Fix "Error Acquiring the State Lock" in Terraform](https://www.devopsness.com/blog/fix-terraform-state-lock) (Kiril Urbonas, DevOpsNess, updated 2026-10-31)

KU

Kiril Urbonas

AI Engineer

585 articles
View all articles by Kiril Urbonas

You might have missed

Evergreen posts worth revisiting.