hashicorp/terraform · error
lock file exists
Error message
lock file %s exists
What it means
remoteClient.Lock first calls cosLock (a COS-side guard), then checks whether the lock info object already exists at c.lockFile via getObject. If a prior run left a lock info object there, exists==true and the lock is rejected. This means another run (live or stale) is holding the state lock.
Solutions
- Confirm no active run, then `terraform force-unlock <LOCK_ID>` (the ID is the md5 stored on the lock object).
- If force-unlock can't reach it, manually delete the lock object from COS at c.lockFile.
- Coordinate team access so only one run targets the workspace at a time.
Defensive patterns
Strategy: validation
Validate before calling
// Pre-check whether a COS lock object is present before attempting Lock.
func lockFileExists(c *remoteClient) (bool, error) {
exists, _, _, err := c.getObject(c.lockFile)
if err != nil {
return false, err
}
return exists, nil
}
// if exists, prompt for force-unlock instead of letting Lock fail. Try / catch
// On lock failure, classify and offer force-unlock.
id, err := c.Lock(info)
if err != nil {
var le *statemgr.LockError
if errors.As(err, &le) {
return fmt.Errorf("state already locked (id=%s); run terraform force-unlock %s", le.Info.ID, le.Info.ID)
}
return err
} Prevention
- Coordinate team access so only one run targets each workspace.
- Always let Terraform clean up its lock; don't kill -9 without force-unlock.
- Wire terraform force-unlock into CI recovery steps.
- Monitor the bucket for stale lock objects.
When it happens
Trigger: Lock() -> cosLock succeeds -> getObject(c.lockFile) returns exists==true.
Common situations: Another Terraform run currently holds the lock; a previous run crashed and left a stale lock object; the lock object was written by an older Terraform version and never cleaned up.
Related errors
- Failed to lock cos state
- lock id mismatch, !=
- Unlocking the state file on TencentCloud cos backend…
- acl value invalid, expected
- default state is not allowed to be deleted
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/5a25850c10d266cb.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/cos/client.go:98
}
// Lock lock remote state file for writing
func (c *remoteClient) Lock(info *statemgr.LockInfo) (string, error) {
log.Printf("[DEBUG] lock remote state file %s", c.lockFile)
err := c.cosLock(c.bucket, c.lockFile)
if err != nil {
return "", c.lockError(err)
}
defer c.cosUnlock(c.bucket, c.lockFile)
exists, _, _, err := c.getObject(c.lockFile)
if err != nil {
return "", c.lockError(err)
}
if exists {
return "", c.lockError(fmt.Errorf("lock file %s exists", c.lockFile))
}
info.Path = c.lockFile
data, err := json.Marshal(info)
if err != nil {
return "", c.lockError(err)
}
check := fmt.Sprintf("%x", md5.Sum(data))
err = c.putObject(c.lockFile, data)
if err != nil {
return "", c.lockError(err)
}
return check, nil
}
// Unlock unlock remote state fileView on GitHub (pinned to d32a084675)