Error: Error locking state: ConditionalCheckFailedException on the DynamoDB lock table
Fixes Terraform's "Error: Error locking state ... ConditionalCheckFailedException" on S3+DynamoDB backends: wait for genuine holders, force-unlock stale locks, or delete the DynamoDB lock row directly. Use after crashed or cancelled runs. Never force-unlock a genuinely running operation.
TL;DR
Another process holds the state lock, or a crashed process left a stale one in DynamoDB. If a run is genuinely active, wait. If the lock is stale, terraform force-unlock [ID] with the ID from the error; if even that fails, delete the lock row from DynamoDB directly.
The error
Error: Error locking state: Error acquiring the state lock:
ConditionalCheckFailedException: The conditional request failed
Lock Info:
ID: terraform-s3-bucket/path/to/terraform.tfstate-md5
Path: terraform-s3-bucket/path/to/terraform.tfstate
Operation: OperationTypePlan
Who: root@runner-123-concurrent-0Steps to fix
- Check whether the holder is still alive. The
WhoandCreatedfields name the machine and time. If a CI job or teammate is mid-run, wait for it.
- Expected: the other run finishes and your retry succeeds.
- If the holder is gone (crashed runner, killed process), force-unlock with the exact ID from the error:
terraform force-unlock terraform-s3-bucket/path/to/terraform.tfstate-md5- Expected:
Terraform state has been successfully unlocked!
- If force-unlock fails (malformed lock row, CLI cannot reach the backend), delete the row directly:
aws dynamodb delete-item --table-name terraform-state-lock \
--key '{"LockID": {"S": "your-bucket/path/terraform.tfstate"}}' \
--region us-east-1- Expected: the scan of the table no longer shows the lock.
- Run
terraform planto confirm the state is healthy before any apply.
- Expected: plan runs without lock errors.
When to use this
- Any Terraform command fails with
Error locking state ... ConditionalCheckFailedExceptionon an S3+DynamoDB backend, especially after a cancelled CI job or crashed laptop.
When NOT to use this
Error acquiring the state lockon a local backend is a lock file on disk, not DynamoDB. Never force-unlock while another operation is genuinely running: you can corrupt state.
Compatibility
- Terraform 0.9+ through 1.x with the S3 backend
dynamodb_tableoption. Terraform 1.10+ offers S3-native locking viause_lockfile = trueas an alternative.
Root cause
DynamoDB implements the lock as a conditional write: acquiring the lock succeeds only if no lock row exists. ConditionalCheckFailedException means the row already exists, i.e. someone (or something crashed mid-run) holds it. Crashed processes never run their unlock, so the row goes stale.
Edge cases
- Two CI jobs racing on the same workspace produce this constantly; serialize them or split state per environment.
terraform force-unlockrefuses without the exact lock ID; copy it from the error, not from memory.- As a last resort on the machine that held the lock: kill Terraform processes and remove local
.terraform.tfstate.lock.infofiles, then verify withps.
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.