Error Acquiring the State Lock, and the Four Things Not to Do
A stuck Terraform lock stops every deploy in the company, and the obvious fix, force unlock, is also the dangerous one.
The message is familiar to anyone who runs Terraform in a team.
Error: Error acquiring the state lock
Lock Info:
ID: 7f3a...
Path: s3://bucket/state/terraform.tfstate
Operation: OperationTypeApply
Who: ci-runner-12@build-agent
Created: 2026-09-09 02:14:11 UTC
And now nothing deploys. Every pipeline queues behind a lock held by a run that is no longer visible. The pressure to "just unlock it" is immediate and enormous, which is exactly why this is a good moment to slow down.
The lock exists to stop two writers from corrupting the state. Removing it while a writer is alive creates the corruption the lock was preventing. So the first question is never "how do I unlock". It is "is the holder actually dead".
This was Terraform 1.7 with an S3 backend and DynamoDB locking. The reasoning applies to any backend with locking.
Read the lock before touching it
The lock info is a small incident report. Four fields matter.
Who and Created. A lock created four minutes ago by a runner that is currently executing is not stuck. It is working. The single most common cause of "stuck lock" reports is a long running apply that is simply slow, often one waiting on a resource that is itself waiting, like a health check that will never pass. Check the CI job first.
Operation. An apply holds the lock longer than a plan. If the operation is apply, assume real changes may be in flight.
ID. This is the key you would use to force unlock, and also the thing to cross reference against the backend to confirm the lock object matches.
So the first move is to look at the pipeline. If the job is running, wait or cancel the job through the CI system, which releases the lock properly. That is always preferable to unlocking from the outside.
The four things not to do
Do not force unlock while the job might be alive. If the apply is still running and you remove the lock, you now have two writers. The second apply reads state that the first is about to overwrite. The result is orphaned resources, state that does not match reality, and an afternoon of reconciliation. This is the corruption the lock exists to prevent, delivered by your own hand.
Do not delete the state file. In panic, someone sometimes deletes or moves the state object to "reset" things. The lock is not in the state file, so this does not unlock anything, and it removes the only record of your infrastructure. This is the worst outcome available and it is chosen surprisingly often under pressure.
Do not retry the apply in a second runner to see if it works. It will not, it will queue on the same lock, and now you have two jobs that will both proceed the moment the lock is released, which is the two writer problem again.
Do not make force unlock a habit. If locks are stuck weekly, the defect is in how runs are managed, and unlocking weekly papers over it. The lock is a symptom. Fix the thing that leaves locks behind.
The safe sequence
When you have confirmed the holder is dead, remove the lock through the tool, not by editing backend objects.
First, find the dead job. Confirm the CI run was cancelled or the process was killed. If the machine that held the lock is gone, the lock is by definition stale.
Then use the supported command with the specific lock ID:
terraform force-unlock 7f3a...
It will ask for confirmation. Give the exact ID. This removes only that lock, not any other.
For S3 plus DynamoDB, the lock is an item in the table, and force-unlock deletes it. If for some reason the CLI cannot, the same item can be removed in the console, but go through the CLI first, because it validates the ID against the backend.
After unlocking, run a terraform plan before any apply and read it carefully. A clean plan with no unexpected destroys is your confirmation that the state survived intact. If the plan shows surprising changes, stop, because that is the two writer damage and it needs reconciliation, not an apply.
Why locks get left behind
A few root causes recur, and each has a real fix.
A killed runner. CI cancels a job mid apply, the process dies without releasing the lock. Fix it by letting CI send the interrupt Terraform understands, so it releases the lock on shutdown, and by giving jobs a graceful stop window.
A hung apply. The apply waits forever on a resource. Fix it with timeouts on the provider operations and on the CI job, so a hang becomes a failure that releases the lock instead of a zombie that holds it.
A crash. The process segfaults or the node dies. Nothing can release the lock from inside. This is the legitimate stale lock case, and it is exactly what the Who and Created fields are for, so a human can judge.
Manual runs. Someone ran Terraform from a laptop against the shared state, it got interrupted, and now CI is blocked. Fix it by making the backend credentials available only to CI, so laptops cannot take the lock.
If it happens again
The lock is correct behaviour doing its job, and the pressure to remove it is the real hazard. Confirm the holder is dead using the lock's own evidence, unlock by ID through the tool, then verify with a careful plan. And if you are unlocking the same lock every week, spend the afternoon on the root cause, because the lock is the only part of that loop that is working.
The same "the safety mechanism is being bypassed under pressure" pattern shows up in deployment gates and canaries, covered in a canary that shipped a broken release anyway.
Published on debugly.dev by Rohit Bhadani | Founder of Krova Cloud