Terraform "Error acquiring the state lock: ConditionalCheckFailedException" in CI
The S3 backend writes a lock item to DynamoDB before touching state. ConditionalCheckFailedException means that item already exists, so another apply holds the lock or a previous run died without releasing it. Terraform refuses to proceed.
What this error means
plan or apply stops with "Error acquiring the state lock" and a DynamoDB ConditionalCheckFailedException, printing the Lock Info: ID, Who, Created, and the lock path.
Error: Error acquiring the state lock
Error message: operation error DynamoDB: PutItem, ConditionalCheckFailedException:
The conditional request failed
Lock Info:
ID: 9b2c1a44-7f0e-4d8a-...
Path: my-tf-state/env/prod/terraform.tfstate
Operation: OperationTypeApply
Who: runner@fv-az123Common causes
Another run currently holds the lock
Two pipelines (or a manual run and CI) target the same state at once. The first wrote the DynamoDB lock item; the second cannot create it, so the conditional put fails.
A crashed run left a stale lock item behind
A cancelled job or a killed runner never ran the release, so the lock item persists in DynamoDB and blocks every later run.
How to fix it
Wait for the holder, then force-unlock if it is dead
- Confirm from the Lock Info Who/Created that no live run still holds it.
- If the holder crashed, release the lock with the exact ID from the error.
- Re-run plan or apply once the lock is gone.
terraform force-unlock 9b2c1a44-7f0e-4d8a-...Serialize state-mutating jobs
Use a concurrency group so only one apply touches a given state at a time, which prevents the lock contention rather than recovering from it.
concurrency:
group: terraform-${{ github.ref }}
cancel-in-progress: falseHow to prevent it
- Run apply behind a concurrency group keyed to the workspace or branch.
- Add a cleanup step or trap so cancelled jobs release the lock.
- Use distinct state keys per environment so unrelated runs never contend.