hashicorp/terraform · warning · statemgr.LockError
%s (lock ID: "%s/%s")
Error message
%s (lock ID: "%s/%s")
What it means
Lock calls Workspaces.Lock; when the API returns tfe.ErrWorkspaceLocked the workspace is already held by another run/process. The error is augmented with the lock ID "<organization>/<workspace-name>" so the holder can be identified or force-unlocked.
Source
Thrown at internal/cloud/state.go:352
func (s *State) Lock(info *statemgr.LockInfo) (string, error) {
s.mu.Lock()
defer s.mu.Unlock()
if s.disableLocks {
return "", nil
}
ctx := context.Background()
lockErr := &statemgr.LockError{Info: s.lockInfo}
// Lock the workspace.
_, err := s.tfeClient.Workspaces.Lock(ctx, s.workspace.ID, tfe.WorkspaceLockOptions{
Reason: tfe.String("Locked by Terraform"),
})
if err != nil {
if err == tfe.ErrWorkspaceLocked {
lockErr.Info = info
err = fmt.Errorf("%s (lock ID: \"%s/%s\")", err, s.organization, s.workspace.Name)
}
lockErr.Err = err
return "", lockErr
}
s.lockInfo = info
return s.lockInfo.ID, nil
}
// statemgr.Refresher impl.
func (s *State) RefreshState() error {
s.mu.Lock()
defer s.mu.Unlock()
return s.refreshState()
}
// refreshState is the main implementation of RefreshState, but split out soView on GitHub (pinned to c9def3e214)
Solutions
- Wait for the in-flight run to finish, then retry.
- In the HCP/TFE UI, confirm what holds the lock.
- If orphaned, run terraform force-unlock "<org>/<workspace>" using the lock ID shown.
- Serialize CI jobs that target the workspace.
Example fix
# before: two pipelines apply the same workspace at once -> lock contention # after: force-unlock the orphaned lock, then run serially terraform force-unlock "my-org/my-workspace" terraform apply
Defensive patterns
Strategy: retry
Validate before calling
// Check whether the workspace is already locked before acquiring
ws, err := client.Workspaces.Read(ctx, org, name)
if err == nil && ws.Locked {
return fmt.Errorf("workspace already locked (lock ID: %s/%s); wait or force-unlock", org, name)
} Type guard
func isWorkspaceAlreadyLocked(err error) bool {
return errors.Is(err, tfe.ErrWorkspaceLocked)
} Try / catch
if _, err := state.Lock(info); err != nil {
var lockErr *statemgr.LockError
if errors.As(err, &lockErr) && errors.Is(lockErr.Err, tfe.ErrWorkspaceLocked) {
// back off and retry a bounded number of times, or surface force-unlock guidance
}
} Prevention
- Serialize CI jobs per workspace
- Inspect the HCP UI before force-unlocking
- Ensure errored runs release their locks
When it happens
Trigger: Two Terraform operations target the same HCP/TFE workspace concurrently, a previous run crashed leaving the workspace locked, or a manual/UI lock is held.
Common situations: CI pipelines running in parallel against one workspace, a prior apply that errored and left the lock orphaned, or someone locked the workspace in the HCP UI.
Related errors
- Already locked for workspace creation: %s
- Workspace is already locked: %s
- Cannot lock workspace; already locked for workspace creation
- failed to lock azure state: %s
- state blob is already locked
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/d1f1e165803cdefd.
Report an issue: GitHub.