{"record":{"id":"7704e230127e89bb","repo":"hashicorp/terraform","slug":"s-lock-id-s-s","errorCode":null,"errorMessage":"%s (lock ID: \"%s/%s\")","messagePattern":"(.+?) \\(lock ID: \"(.+?)/(.+?)\"\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote/backend_state.go","lineNumber":192,"sourceCode":"// by implementing remote.ClientForcePusher\nfunc (r *remoteClient) EnableForcePush() {\n\tr.forcePush = true\n}\n\n// Lock the remote state.\nfunc (r *remoteClient) Lock(info *statemgr.LockInfo) (string, error) {\n\tctx := context.Background()\n\n\tlockErr := &statemgr.LockError{Info: r.lockInfo}\n\n\t// Lock the workspace.\n\t_, err := r.client.Workspaces.Lock(ctx, r.workspace.ID, tfe.WorkspaceLockOptions{\n\t\tReason: tfe.String(\"Locked by Terraform\"),\n\t})\n\tif err != nil {\n\t\tif err == tfe.ErrWorkspaceLocked {\n\t\t\tlockErr.Info = info\n\t\t\terr = fmt.Errorf(\"%s (lock ID: \\\"%s/%s\\\")\", err, r.organization, r.workspace.Name)\n\t\t}\n\t\tlockErr.Err = err\n\t\treturn \"\", lockErr\n\t}\n\n\tr.lockInfo = info\n\n\treturn r.lockInfo.ID, nil\n}\n\n// Unlock the remote state.\nfunc (r *remoteClient) Unlock(id string) error {\n\tctx := context.Background()\n\n\t// We first check if there was an error while uploading the latest\n\t// state. If so, we will not unlock the workspace to prevent any\n\t// changes from being applied until the correct state is uploaded.\n\tif r.stateUploadErr {","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/backend/remote/backend_state.go#L174-L210","documentation":"Emitted by remoteClient.Lock (backend_state.go:189-196) when Workspaces.Lock fails specifically with tfe.ErrWorkspaceLocked. The message enriches the underlying 'workspace is already locked' error with the canonical lock ID formed as \"organization/workspace\" so the user knows which workspace holds the lock and can force-unlock if needed.","triggerScenarios":"Acquiring a state lock when the workspace is already locked — a previous run/process crashed without unlocking, another team member holds the lock, or a prior operation set stateUploadErr leaving the lock in place.","commonSituations":"CI runner killed mid-apply leaving a stale lock; local `terraform apply` interrupted (Ctrl-C) before unlock; previous state upload error deliberately retained the lock (see error 427); two developers running against the same workspace concurrently.","solutions":["Confirm no operation is genuinely running, then run `terraform force-unlock \"<organization>/<workspace>\"` using the lock ID from the message.","If another operation is legitimately running, wait for it to finish rather than force-unlocking.","Investigate why the previous lock was not released (crashed CI, killed process) to prevent recurrence.","Use `-lock-timeout=<duration>` so Terraform waits and retries instead of failing immediately."],"exampleFix":"// before\nError: workspace is already locked (lock ID: \"myorg/dev\")\n\n// after: confirm no live run, then force-unlock with the shown ID\n$ terraform force-unlock \"myorg/dev\"","handlingStrategy":"try-catch","validationCode":"// Preflight: fail fast if the workspace is already locked.\nif ws, err := r.client.Workspaces.Read(ctx, r.organization, r.workspace.Name); err == nil && ws.Locked {\n    return fmt.Errorf(\"workspace %s/%s already locked; run force-unlock if stale\", r.organization, r.workspace.Name)\n}","typeGuard":null,"tryCatchPattern":"// On 'already locked', surface the canonical lock id so the operator can force-unlock.\n_, err := r.client.Workspaces.Lock(ctx, r.workspace.ID, tfe.WorkspaceLockOptions{Reason: tfe.String(\"Locked by Terraform\")})\nif err != nil {\n    if err == tfe.ErrWorkspaceLocked {\n        err = fmt.Errorf(\"%s (lock ID: %q/%q)\", err, r.organization, r.workspace.Name)\n    }\n    return \"\", &statemgr.LockError{Info: info, Err: err}\n}","preventionTips":["Use `-lock-timeout=<duration>` so Terraform waits instead of failing immediately on contention.","Ensure CI runners fully complete (unlock) before timeout/kill to avoid stale locks.","Keep one writer per workspace to avoid lock contention.","After crashes, confirm no live run holds the lock before force-unlocking."],"tags":["state","lock","concurrency","force-unlock","terraform-cloud"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T20:17:04.800Z"}