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.
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.
- Run 1took the lock (or crashed holding it)holds
- State lockLock Info: who, whenrefused(fails)
- Run 2Error acquiring the state lock(breaks here)
TL;DR
- Read
Who,OperationandCreated. - If that run is still going: wait (or cancel it properly).
- Only if you’re sure nothing is running anymore:
terraform force-unlock 6b8e2a1c-4f0d-9e7b-2c55-91a3d0f4b812terraform plan # check the state is consistent1. Read the lock info
- Who — the user and machine that took the lock.
runner@ci-runner-7is a CI job;alex@alex-laptopis a colleague. - Operation —
OperationTypeApplychanges infrastructure;OperationTypePlanonly 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:
terraform apply -lock-timeout=10mEven 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: false3. 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:
terraform force-unlock 6b8e2a1c-4f0d-9e7b-2c55-91a3d0f4b812Terraform 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:
terraform planIf 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:
terraform state push errored.tfstateWhere 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.