{"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":"warning","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/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/backend/remote/backend_state.go#L174-L210","documentation":"remoteClient.Lock wraps Workspaces.Lock. When TFC returns ErrWorkspaceLocked (already locked), the backend augments the error with the org/workspace lock-ID string so the user knows what to unlock. The augmented error is wrapped in a statemgr.LockError.","triggerScenarios":"Workspaces.Lock returns tfe.ErrWorkspaceLocked: another `terraform plan/apply`, a TFC run, or a crashed process holds the workspace lock; a previous run died without unlocking.","commonSituations":"Concurrent CI jobs on the same workspace; long-running TFC apply still in progress; lock left behind after a killed process (requires force-unlock).","solutions":["Wait for the in-flight run/operation to finish and release the lock.","If the lock is stale, run `terraform force-unlock \"<org>/<workspace>\"` using the ID from the error.","Serialize concurrent runs on the same workspace via TFC queues or external locking."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Pre-check lock state to give a friendlier error\nws, _ := c.Workspaces.Read(ctx, org, name)\nif ws.Locked { return fmt.Errorf(\"workspace %s/%s already locked; check active runs\", org, name) }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Serialize runs on a shared workspace.","Use `terraform force-unlock \"<org>/<ws>\"` only after confirming the lock is stale.","Monitor for orphaned locks after CI job kills."],"tags":["terraform","remote-backend","tfe","locking","workspace","concurrency"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}