Terraform apply fails with state lock after CI job is canceled

2026-11-13 · 3 min read

Terraform apply fails with state lock after CI job is canceled

A canceled CI job in the middle of terraform apply leaves the workspace locked. The next pipeline run dies in seconds with Error acquiring the state lock, prints a lock ID and creation timestamp, and blocks every teammate touching the same state. If two engineers retry, the second failure looks like a recurring outage. Clearing the Terraform state lock canceled CI jobs leave behind is usually a five-minute fix — but only if you can prove the original process is gone.

What the lock record actually is

Terraform's S3 backend writes a lock item to DynamoDB under LockID; the GCS backend writes a .tflock object; the Postgres backend takes an advisory lock; Terraform Cloud uses its own run lock [link needed]. Canceling a job sends SIGTERM, then SIGKILL after a grace period, and a killed process never runs its deferred unlock [link needed]. Those records have no built-in expiry [link needed]. The lock error's Lock Info block includes the ID, state path, operation, who created it, and when — copy those fields before touching anything.

Recovering from a Terraform state lock canceled CI left behind

1. Confirm the holder is dead. Check the runner or pod, the Terraform Cloud run, and any local terraform apply a teammate started. A "canceling" job can still be alive.

2. Copy the lock ID from the error output. Do not reconstruct or guess it.

3. Inspect the backend for the matching record: the DynamoDB LockID item, the .tflock object in the bucket, or a pg_locks row.

4. Force-unlock from the same directory and backend config: terraform force-unlock <LOCK_ID> [link needed]. Confirm the prompt only after step 3 matches.

5. Re-run terraform plan before apply. A killed apply can leave infrastructure half-created; the plan shows the delta so you know what the next apply will change.

Use terraform force-unlock -force <LOCK_ID> only in automation, where no human reads the prompt.

Preventing the next Terraform state lock canceled CI incident

When NOT to do this

Do not force-unlock when you cannot verify the original process is dead. A draining autoscaler, a network partition between runner and backend, or a job marked "canceled" in the UI can still have a live Terraform process. Two applies writing one state can interleave writes and corrupt it [link needed]. For Terraform Cloud, if the lock belongs to another run, cancel or discard that run first; CLI force-unlock may not clear a remote run lock [link needed]. Skip the whole procedure if the lock was placed intentionally for a change freeze and no CI job is involved.

Checklist before force-unlocking a Terraform state lock canceled CI left behind