hashicorp/terraform · error
%s (lock ID: "%s/%s")
Error message
%s (lock ID: "%s/%s")
What it means
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.
Source
Thrown at internal/backend/remote/backend_state.go:192
// by implementing remote.ClientForcePusher
func (r *remoteClient) EnableForcePush() {
r.forcePush = true
}
// Lock the remote state.
func (r *remoteClient) Lock(info *statemgr.LockInfo) (string, error) {
ctx := context.Background()
lockErr := &statemgr.LockError{Info: r.lockInfo}
// Lock the workspace.
_, err := r.client.Workspaces.Lock(ctx, r.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, r.organization, r.workspace.Name)
}
lockErr.Err = err
return "", lockErr
}
r.lockInfo = info
return r.lockInfo.ID, nil
}
// Unlock the remote state.
func (r *remoteClient) Unlock(id string) error {
ctx := context.Background()
// We first check if there was an error while uploading the latest
// state. If so, we will not unlock the workspace to prevent any
// changes from being applied until the correct state is uploaded.
if r.stateUploadErr {View on GitHub (pinned to c9def3e214)
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.
Example fix
// before Error: workspace is already locked (lock ID: "myorg/dev") // after: confirm no live run, then force-unlock with the shown ID $ terraform force-unlock "myorg/dev"
Defensive patterns
Strategy: try-catch
Validate before calling
// Preflight: fail fast if the workspace is already locked.
if ws, err := r.client.Workspaces.Read(ctx, r.organization, r.workspace.Name); err == nil && ws.Locked {
return fmt.Errorf("workspace %s/%s already locked; run force-unlock if stale", r.organization, r.workspace.Name)
} Try / catch
// On 'already locked', surface the canonical lock id so the operator can force-unlock.
_, err := r.client.Workspaces.Lock(ctx, r.workspace.ID, tfe.WorkspaceLockOptions{Reason: tfe.String("Locked by Terraform")})
if err != nil {
if err == tfe.ErrWorkspaceLocked {
err = fmt.Errorf("%s (lock ID: %q/%q)", err, r.organization, r.workspace.Name)
}
return "", &statemgr.LockError{Info: info, Err: err}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- error uploading state: %v
- lock ID %q does not match existing lock ID "%s/%s"
- lock ID does not match existing lock
- Error locking state: %s
- failed to lock azure state: %s
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/7704e230127e89bb.
Report an issue: GitHub.