Quick Answer
# Check if another terraform process is running
ps aux | grep terraform
# If no process is running, force unlock
terraform force-unlock LOCK_ID
The Error
Error: Error acquiring the state lock
Error message: ConditionalCheckFailedException: The conditional request failed
Lock Info:
ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Path: my-bucket/prod/terraform.tfstate
Operation: OperationTypeApply
Who: user@machine
Version: 1.9.0
Created: 2026-04-12 10:30:00.000000000 +0000 UTC
Terraform acquires a state lock to protect against two processes
writing to the state at the same time. The lock was not released
within the configured timeout period.
What Causes This
- Another terraform process is running — someone else on the team is applying
- Previous run crashed — Ctrl+C, network drop, or CI/CD runner killed mid-apply
- Lock timeout too short — large deployments take longer than the default timeout
- CI/CD running parallel jobs — multiple pipelines targeting the same state
Solution 1: Wait and Retry
The most common case — someone else is running terraform. Wait for them to finish:
# Check who holds the lock (shown in error message)
# Who: user@machine — ask that person
# Retry with a longer timeout
terraform apply -lock-timeout=10m
Solution 2: Force Unlock a Stuck Lock
If no terraform process is running (crashed or killed):
# Copy the Lock ID from the error message
terraform force-unlock a1b2c3d4-e5f6-7890-abcd-ef1234567890
Before force-unlocking, verify no process is actually running:
# Local machine
ps aux | grep terraform
# CI/CD — check running pipelines in GitLab/GitHub
# Check DynamoDB directly (S3 backend)
aws dynamodb scan \
--table-name terraform-locks \
--filter-expression "LockID = :id" \
--expression-attribute-values '{":id":{"S":"my-bucket/prod/terraform.tfstate"}}'
Solution 3: Prevent Parallel Runs in CI/CD
GitLab CI — use resource_group:
apply-prod:
stage: deploy
resource_group: terraform-prod # Only one job at a time
script:
- terraform apply -auto-approve -lock-timeout=5m
GitHub Actions — use concurrency:
jobs:
apply:
concurrency:
group: terraform-prod
cancel-in-progress: false # Don't cancel running applies!
steps:
- run: terraform apply -auto-approve
Solution 4: Configure Lock Timeout
# Default timeout is 0s (fail immediately)
# Set a reasonable timeout for team environments
terraform apply -lock-timeout=5m
terraform plan -lock-timeout=5m
Or in CI/CD:
export TF_CLI_ARGS_apply="-lock-timeout=5m"
export TF_CLI_ARGS_plan="-lock-timeout=5m"
How State Locking Works
| Backend | Lock Mechanism |
|---|---|
| S3 | DynamoDB table |
| GCS | Native object locking |
| Azure Blob | Blob lease |
| Terraform Cloud | Built-in |
| Consul | KV lock |
| Local | .terraform.tfstate.lock.info file |
S3 + DynamoDB Setup
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks" # This enables locking
encrypt = true
}
}
# Create the lock table
resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}
Troubleshooting Checklist
- Is another terraform process actually running? → Wait or coordinate
- Did a CI/CD runner crash? → Check pipeline status, then
force-unlock - Using S3 backend without DynamoDB table? → Add
dynamodb_tableconfig - Multiple workspaces on same lock table? → That’s fine, locks are per-state-file
- Still failing after force-unlock? → Check IAM permissions on DynamoDB table
Hands-On Courses
- Terraform for Beginners on CopyPasteLearn
- Terraform By Example — practical code examples
Conclusion
State lock errors mean another terraform process holds the lock or a previous run crashed. Check for running processes first. If the lock is stale, use terraform force-unlock LOCK_ID. Prevent the issue with CI/CD resource_group or concurrency controls and set -lock-timeout=5m in team environments.
