{"record":{"id":"e05ee4e97ff44011","repo":"hashicorp/terraform","slug":"failed-to-lock-azure-state-s","errorCode":null,"errorMessage":"failed to lock azure state: %s","messagePattern":"failed to lock azure state: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/azure/backend_state.go","lineNumber":119,"sourceCode":"\t\taccountName:        b.accountName,\n\t\tsnapshot:           b.snapshot,\n\t}\n\n\tstateMgr := &remote.State{Client: client}\n\n\t// Grab the value\n\tif err := stateMgr.RefreshState(); err != nil {\n\t\treturn nil, diags.Append(err)\n\t}\n\t//if this isn't the default state name, we need to create the object so\n\t//it's listed by States.\n\tif v := stateMgr.State(); v == nil {\n\t\t// take a lock on this state while we write it\n\t\tlockInfo := statemgr.NewLockInfo()\n\t\tlockInfo.Operation = \"init\"\n\t\tlockId, err := client.Lock(lockInfo)\n\t\tif err != nil {\n\t\t\treturn nil, diags.Append(fmt.Errorf(\"failed to lock azure state: %s\", err))\n\t\t}\n\n\t\t// Local helper function so we can call it multiple places\n\t\tlockUnlock := func(parent error) error {\n\t\t\tif err := stateMgr.Unlock(lockId); err != nil {\n\t\t\t\treturn fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err)\n\t\t\t}\n\t\t\treturn parent\n\t\t}\n\n\t\t// Grab the value\n\t\tif err := stateMgr.RefreshState(); err != nil {\n\t\t\terr = lockUnlock(err)\n\t\t\treturn nil, diags.Append(err)\n\t\t}\n\t\t//if this isn't the default state name, we need to create the object so\n\t\t//it's listed by States.\n\t\tif v := stateMgr.State(); v == nil {","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/backend/remote-state/azure/backend_state.go#L101-L137","documentation":"Raised in Backend.StateMgr (backend_state.go:117-119) when initializing a brand-new workspace state blob. Because the blob does not yet exist, Terraform acquires a lease (client.Lock) to safely create it; if Lock returns an error (another process already holds the lease, or the lease acquire data-plane call failed), this error wraps the resulting statemgr.LockError which also carries the existing lock info.","triggerScenarios":"Produced at backend_state.go:117-119 the first time a non-existent workspace state is opened (e.g. 'terraform workspace new' or 'terraform init' for a fresh key) when client.Lock fails — typically because properties.LeaseStatus == Locked (error 154) or AcquireLease failed.","commonSituations":"Two concurrent 'terraform apply' runs on the same new workspace; a previous run crashed holding the lease; CI retried a job while the first was still running; a stale lease left by a terminated process.","solutions":["Wait for the other Terraform process to finish and release the lease, then retry","Identify the lock holder and run 'terraform force-unlock <lock-id>' with the ID from the error message","Use the Azure portal / az storage blob lease break to release a stale lease: az storage blob lease break --account-name <account> -c <container> -b <key>","Ensure only one writer runs per workspace (CI queue/locking) to avoid contention"],"exampleFix":"# the error reports the existing lock info; force-unlock with its id\nterraform force-unlock a1b2c3d4-e5f6-7890-abcd-ef1234567890\n\n# or break the lease directly in Azure\naz storage blob lease break \\\n  --account-name mystage --auth-mode login \\\n  -c tfstate -b \"prod.terraform.tfstate\"","handlingStrategy":"retry","validationCode":"# Before opening a fresh workspace, check whether its state blob is lease-locked\nKEY=\"${ARM_KEY}env:${WS_NAME}\"\nSTATUS=$(az storage blob show --account-name \"$ARM_STORAGE_ACCOUNT_NAME\" -c \"$ARM_CONTAINER_NAME\" -n \"$KEY\" --query 'properties.lease.status' -o tsv 2>/dev/null)\n[ \"$STATUS\" = \"locked\" ] && echo \"WARN: blob already locked -> error 151 likely; wait or force-unlock\" || echo \"OK: not locked\"","typeGuard":null,"tryCatchPattern":"# Bash: on lock failure, surface the lock id and offer force-unlock\nrun_with_lock_guard() {\n  if ! terraform \"$@\" 2>err.log; then\n    if grep -q \"failed to lock azure state\" err.log; then\n      LID=$(grep -oE '[0-9a-f-]{36}' err.log | head -1)\n      echo \"State locked. Review holder, then: terraform force-unlock $LID\"\n    fi\n    return 1\n  fi\n}","preventionTips":["Serialize CI per workspace so only one run acquires the lease","On a crashed run, inspect the lock info and force-unlock before retrying","Use 'az storage blob lease status' checks in pipelines for shared workspaces"],"tags":["azure","state","locking","lease","concurrency"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T20:17:04.800Z"}