hashicorp/terraform · error · statemgr.LockError

lock file %s exists

Error message

lock file %s exists

What it means

Raised by remoteClient.Lock() (cos/client.go:98) after the COS tag lock is acquired. Lock() then checks whether the lock file object already exists in the bucket; if it does, another Terraform run already holds the state lock, so this run must abort and release its tag lock.

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 file

View on GitHub (pinned to c9def3e214)

Solutions

  1. Wait for the other terraform run to finish and release the lock, then retry.
  2. If the holder is gone, run `terraform force-unlock <lock-id>` to remove the stale lock file.
  3. As a last resort, delete the lock file object and the tencentcloud-terraform-lock tag in the COS console, then retry.

Example fix

// after 'lock file ... exists'
terraform force-unlock <lock-id>
terraform apply
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check whether a lock file already exists before attempting Lock
func alreadyLocked(c *remoteClient) bool {
    exists, _, _, _ := c.getObject(c.lockFile)
    return exists
}

Try / catch

// Retry Lock when contention is transient; force-unlock if stale
for attempt := 0; attempt < 3; attempt++ {
    _, err := c.Lock(info)
    if err == nil { break }
    if !strings.Contains(err.Error(), "lock file") { return err }
    time.Sleep(time.Duration(attempt+1) * 2 * time.Second)
}

Prevention

When it happens

Trigger: Lock() calls getObject(c.lockFile) and exists==true, meaning the <state>.tflock object is present from a prior/active lock holder.

Common situations: Concurrent terraform runs on the same workspace; a previous run crashed and left the lock file object behind; a force-unlock was not performed after a crash.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/5a25850c10d266cb. Report an issue: GitHub.