{"record":{"id":"5f7147e6d3f8c938","repo":"hashicorp/terraform","slug":"error-locking-state-s","errorCode":null,"errorMessage":"Error locking state: %s","messagePattern":"Error locking state: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/command/meta_backend.go","lineNumber":1778,"sourceCode":"\t\t\tfor _, localState := range localStates {\n\t\t\t\t// We always delete the local state, unless that was our new state too.\n\t\t\t\tif err := localState.WriteState(nil); err != nil {\n\t\t\t\t\tdiags = diags.Append(&errBackendMigrateLocalDelete{err})\n\t\t\t\t\treturn nil, diags\n\t\t\t\t}\n\t\t\t\tif err := localState.PersistState(nil); err != nil {\n\t\t\t\t\tdiags = diags.Append(&errBackendMigrateLocalDelete{err})\n\t\t\t\t\treturn nil, diags\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif m.stateLock {\n\t\tview := views.NewStateLocker(vt, m.View)\n\t\tstateLocker := clistate.NewLocker(m.stateLockTimeout, view)\n\t\tif err := stateLocker.Lock(sMgr, \"backend from plan\"); err != nil {\n\t\t\tdiags = diags.Append(fmt.Errorf(\"Error locking state: %s\", err))\n\t\t\treturn nil, diags\n\t\t}\n\t\tdefer stateLocker.Unlock()\n\t}\n\n\t// Store the metadata in our saved state location\n\ts := sMgr.State()\n\tif s == nil {\n\t\ts = workdir.NewBackendStateFile()\n\t}\n\ts.Backend = &workdir.BackendConfigState{\n\t\tType: c.Type,\n\t\tHash: uint64(cHash),\n\t}\n\terr := s.Backend.SetConfig(configVal, b.ConfigSchema())\n\tif err != nil {\n\t\tdiags = diags.Append(fmt.Errorf(\"Can't serialize backend configuration as JSON: %s\", err))\n\t\treturn nil, diags","sourceCodeStart":1760,"sourceCodeEnd":1796,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/command/meta_backend.go#L1760-L1796","documentation":"In Meta.backend_C_r_s (meta_backend.go:1778) Terraform tried to acquire the state lock (clistate.Locker.Lock) before writing the freshly reconfigured single-state backend into the workdir-state file and failed. The wrapped `%s` is the underlying lock error, typically 'state is locked' with the ID and info of whoever holds it, or a transport error reaching the remote backend.","triggerScenarios":"Another Terraform process (or CI job) currently holds the state lock on the same backend; a previous run crashed without releasing the lock leaving a stale lock object; network/credentials failure contacting the locking backend (S3 DynamoDB, Consul, Azure, GCS, etc.).","commonSituations":"Concurrent CI pipelines targeting the same state, a developer's `terraform apply` still running in another shell, a killed run leaving a dangling lock, expired/temporary cloud credentials while locking.","solutions":["Inspect the wrapped error for the lock holder info; wait for that process to finish or coordinate to stop it.","If the lock is genuinely stale (the holder is gone), run `terraform force-unlock <LOCK_ID>` then retry init.","For network/auth errors, verify credentials, endpoint, and connectivity to the backend, then retry.","Use `-lock-timeout=<duration>` to make init wait for the lock instead of failing immediately."],"exampleFix":"// before: terraform init   (fails: Error locking state: state is locked)\n// after: terraform force-unlock <LOCK_ID> && terraform init","handlingStrategy":"retry","validationCode":"// Before init, confirm no stale lock exists on the backend.\n// (Authoritative check is backend-specific; shown as a preflight concept.)\nfunc ensureStateUnlocked(runner func() error, lockTimeout time.Duration) error {\n    return retry(lockTimeout, time.Second*2, runner) // wait out a transient holder\n}","typeGuard":null,"tryCatchPattern":"// On 'Error locking state', distinguish stale vs active and act accordingly.\nif err := cmd.Init(); err != nil && strings.Contains(err.Error(), \"Error locking state\") {\n    if id := extractLockID(err.Error()); id != \"\" && lockLooksStale(err) {\n        _ = cmd.ForceUnlock(id) // only after confirming the holder is gone\n        return cmd.Init()\n    }\n}","preventionTips":["Use `-lock-timeout=<duration>` in CI so init waits rather than failing on a short-lived lock.","Never run concurrent Terraform commands against the same state.","Ensure crashed runs are cleaned up (force-unlock) before the next run.","Validate backend credentials and connectivity before running init."],"tags":["state","lock","backend","concurrency","init"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}