hashicorp/terraform · error
state blob is already locked
Error message
state blob is already locked
What it means
Returned by RemoteClient.Lock when the blob's GetProperties response has LeaseStatus == blobs.Locked. The backend short-circuits with this message wrapped into a LockError that includes the existing lock info (retrieved via getLockInfo). This is the canonical 'another run holds the state' signal.
Solutions
- Run `terraform force-unlock <id>` using the ID in the LockError info.
- Confirm no legitimate run is in progress before force-unlocking.
- If the lock info cannot be read (see error 158), break the lease via `az storage blob lease break`.
- Coordinate team access to the shared state to prevent recurrence.
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: check lease status before Lock.
func blobLeaseStatus(ctx context.Context, acct, container, blob string) (string, error) {
cmd := exec.CommandContext(ctx, "az", "storage", "blob", "show",
"--account-name", acct, "-c", container, "-n", blob, "--query", "properties.lease.status", "-o", "tsv")
out, err := cmd.Output()
return strings.TrimSpace(string(out)), err
} Try / catch
// Catch LockError, print the holder's lock info, and offer force-unlock.
lockId, err := client.Lock(info)
if err != nil {
var le *statemgr.LockError
if errors.As(err, &le) {
if le.Info != nil {
log.Printf("state already locked by %s (%s); run: terraform force-unlock %s",
le.Info.Who, le.Info.Operation, le.Info.ID)
}
os.Exit(1)
}
return err
} Prevention
- Run a single terraform process per workspace at a time.
- Wire CI to acquire a workspace lock outside terraform if multiple pipelines share state.
- Document `terraform force-unlock` as a runbook step.
When it happens
Trigger: Another terraform process is mid-apply on the same state blob; a previous run crashed leaving a stale lease; the blob was leased out-of-band.
Common situations: Concurrent runs in different terminals / CI pipelines; crashed run left a lease; a teammate's interrupted apply.
Related errors
- blob metadata was empty
- Error unlocking Azure state. Lock ID
- failed to lock azure state
- failed to retrieve lock info
- error snapshotting Blob
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/3c0372796b66718c.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/azure/client.go:196
if !response.WasNotFound(properties.HttpResponse) {
return "", getLockInfoErr(err)
}
// if we don't find the blob, we need to build it
contentType := "application/json"
putGOptions := blobs.PutBlockBlobInput{
ContentType: &contentType,
}
_, err = c.giovanniBlobClient.PutBlockBlob(ctx, c.containerName, c.keyName, putGOptions)
if err != nil {
return "", getLockInfoErr(err)
}
}
// if the blob is already locked then error
if properties.LeaseStatus == blobs.Locked {
return "", getLockInfoErr(fmt.Errorf("state blob is already locked"))
}
leaseID, err := c.giovanniBlobClient.AcquireLease(ctx, c.containerName, c.keyName, leaseOptions)
if err != nil {
return "", getLockInfoErr(err)
}
info.ID = leaseID.LeaseID
c.leaseID = leaseID.LeaseID
if err := c.writeLockInfo(info); err != nil {
return "", err
}
return info.ID, nil
}
func (c *RemoteClient) getLockInfo() (*statemgr.LockInfo, error) {View on GitHub (pinned to d32a084675)