{"record":{"id":"d1f1e165803cdefd","repo":"hashicorp/terraform","slug":"s-lock-id-s-s-d1f1e1","errorCode":null,"errorMessage":"%s (lock ID: \"%s/%s\")","messagePattern":"(.+?) \\(lock ID: \"(.+?)/(.+?)\"\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cloud/state.go","lineNumber":352,"sourceCode":"func (s *State) Lock(info *statemgr.LockInfo) (string, error) {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\tif s.disableLocks {\n\t\treturn \"\", nil\n\t}\n\tctx := context.Background()\n\n\tlockErr := &statemgr.LockError{Info: s.lockInfo}\n\n\t// Lock the workspace.\n\t_, err := s.tfeClient.Workspaces.Lock(ctx, s.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, s.organization, s.workspace.Name)\n\t\t}\n\t\tlockErr.Err = err\n\t\treturn \"\", lockErr\n\t}\n\n\ts.lockInfo = info\n\n\treturn s.lockInfo.ID, nil\n}\n\n// statemgr.Refresher impl.\nfunc (s *State) RefreshState() error {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\treturn s.refreshState()\n}\n\n// refreshState is the main implementation of RefreshState, but split out so","sourceCodeStart":334,"sourceCodeEnd":370,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/cloud/state.go#L334-L370","documentation":"Thrown during Lock when the TFE Workspaces.Lock API returns tfe.ErrWorkspaceLocked, meaning the workspace is already locked. The error message appends the organization and workspace name so the user can identify which workspace holds the conflicting lock. The full error is wrapped in a statemgr.LockError so the caller can surface lock metadata.","triggerScenarios":"A previous terraform run crashed or was killed without releasing its lock; a user manually locked the workspace in the HCP Terraform / TFE UI; another CI job or teammate is currently running apply on the same workspace; a speculative plan holds the lock.","commonSituations":"CI pipeline cancelled mid-apply leaving a dangling lock; developer locked a workspace for investigation and forgot; concurrent terraform runs in branching CI workflows targeting one workspace; TFE run that errored but did not auto-unlock.","solutions":["Check the workspace run queue and lock status in the HCP Terraform / TFE UI to confirm who holds the lock","If the lock is stale (the holder is no longer active), use terraform force-unlock or the UI Force Unlock button","Wait for the current run to complete and release the lock naturally","Investigate CI orchestration to prevent concurrent applies to the same workspace (use workspace-level queueing)"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before calling Lock, check if the workspace is already locked:\nws, err := tfeClient.Workspaces.Read(ctx, organization, workspaceName)\nif err != nil {\n    return err\n}\nif ws.Locked {\n    return fmt.Errorf(\"workspace %s/%s is already locked; run 'terraform force-unlock' if the lock is stale\", organization, workspaceName)\n}","typeGuard":null,"tryCatchPattern":"id, err := stateMgr.Lock(lockInfo)\nif err != nil {\n    var lockErr *statemgr.LockError\n    if errors.As(err, &lockErr) && strings.Contains(err.Error(), \"lock ID:\") {\n        // workspace already locked — prompt user or auto-wait\n        return handleExistingLock(lockErr)\n    }\n    return err\n}","preventionTips":["Prevent concurrent CI jobs targeting the same workspace with orchestration-level locking","After a crashed apply, always run 'terraform force-unlock' or unlock via the TFE UI before the next run","Use workspace auto-queue in HCP Terraform rather than parallel CI pipelines to serialize runs"],"tags":["locking","tfe","workspace","concurrent","terraform"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}