Skip to content
ohsudo, Home
Infrastructure as Code

Terraform "Error acquiring the state lock": who holds it, and when force-unlock is safe

Fix Terraform and OpenTofu "Error acquiring the state lock": read the lock info, wait or find the running job, and use force-unlock only on a stale lock.

Intermediate4 min readApplies to Terraform 1.5+ · OpenTofu 1.6+ · S3 · Azure · GCS and HCP Terraform backends

by OhSudo ··

You run terraform plan or apply and get:

╷
│ Error: Error acquiring the state lock
│
│ Error message: ConditionalCheckFailedException: The conditional request failed
│ Lock Info:
│ ID: 6b8e2a1c-4f0d-9e7b-2c55-91a3d0f4b812
│ Path: acme-tfstate/prod/network/terraform.tfstate
│ Operation: OperationTypeApply
│ Who: runner@ci-runner-7
│ Version: 1.9.8
│ Created: 2026-11-02 08:41:12.413 +0000 UTC
│ Info:
│
│
│ Terraform acquires a state lock to protect the state from being written
│ by multiple users at the same time. Please resolve the issue above and try
│ again. For most commands, you can disable locking with the "-lock=false"
│ flag, but this is not recommended.
╵

The state is Terraform’s record of what it created. Two runs writing it at the same time would corrupt it, so every run takes a lock first and releases it at the end. Another run holds it now — or held it, and died before releasing it. The first line of the error depends on the backend (S3, Azure, GCS…); the Lock Info is what matters.

  1. Run 1took the lock (or crashed holding it)
  2. State lockLock Info: who, when
  3. Run 2Error acquiring the state lock(breaks here)
Every run takes a lock on the state before it starts and releases it at the end. Another run holds it now, or held it and died before releasing it.

TL;DR

  1. Read Who, Operation and Created.
  2. If that run is still going: wait (or cancel it properly).
  3. Only if you’re sure nothing is running anymore:
Terminal window
terraform force-unlock 6b8e2a1c-4f0d-9e7b-2c55-91a3d0f4b812
terraform plan # check the state is consistent

1. Read the lock info

  • Who — the user and machine that took the lock. runner@ci-runner-7 is a CI job; alex@alex-laptop is a colleague.
  • Operation — OperationTypeApply changes infrastructure; OperationTypePlan only reads (but still locks).
  • Created — when. Two minutes ago: probably still running. Two days ago: almost certainly abandoned.

2. Someone is still running

This is the lock doing its job. Check the CI pipelines for that workspace, or ask the person in Who. Let the run finish — or cancel it properly (the cancel button in CI, or one Ctrl+C locally, which lets Terraform stop cleanly, save the state and release the lock).

In CI, make jobs wait for the lock instead of failing right away:

Terminal window
terraform apply -lock-timeout=10m

Even better, never let two runs on the same state start together. In GitHub Actions, a concurrency group per environment does it:

concurrency:
group: terraform-prod-network
cancel-in-progress: false

3. Nobody is running: a stale lock

Locks stay behind when a run dies without cleaning up: a CI job killed by a timeout, a runner that disappeared, a laptop that went to sleep, a second Ctrl+C. Then the lock blocks everybody, forever.

Make sure nothing is running (CI history, the person who held it), then remove it with the ID from the error:

Terminal window
terraform force-unlock 6b8e2a1c-4f0d-9e7b-2c55-91a3d0f4b812

Terraform asks for confirmation (-force skips it — avoid that in scripts). With OpenTofu: tofu force-unlock <ID>.

Warning

Never force-unlock a run that is still going. Two applies on the same state can create duplicate resources and leave a state that no longer matches reality. Same for -lock=false: it doesn’t fix anything, it only removes the protection.

4. After a run that died in the middle

An apply that was killed may have created some resources and not recorded them. Check before anything else:

Terminal window
terraform plan

If the plan wants to create something that already exists, terraform import it (or use an import block) instead of creating a duplicate.

If Terraform couldn’t save the state at the end of the run, it writes it locally as errored.tfstate and tells you so. Push it back once the backend works again:

Terminal window
terraform state push errored.tfstate

Where the lock lives

If you can’t run force-unlock (no permissions, broken setup), it helps to know where the lock is stored — the administrators of that backend can remove it:

Backend The lock is…
S3 with DynamoDB An item in the DynamoDB table, keyed by the state path
S3 with use_lockfile = true (Terraform 1.10+) A .tflock file next to the state in the bucket
Azure Storage A lease on the state blob
Google Cloud Storage A .tflock object next to the state
HCP Terraform / Terraform Enterprise The workspace is “locked” — unlock it in the web UI
Local The file .terraform.tfstate.lock.info

Shell, we have a problem!

Similar, but different: Error loading state: … AccessDenied / 403 — your credentials can’t read the state at all; check the cloud identity, not the lock. Backend configuration changed — run terraform init -reconfigure (or -migrate-state if you really moved the state). For the lock file of your providers, see Inconsistent dependency lock file. Changing the backend? See Backend initialization required.

Found this useful? Share it.

Found a mistake? Let us know at contact@ohsudo.com.

Tools

Try it yourself

Articles

Related articles