hashicorp/terraform · warning

Error unlocking S3 state. Lock ID: %s Error: %s You may ha

Error message

Error unlocking S3 state. Lock ID: %s

Error: %s

You may have to force-unlock this state in order to use it again.

What it means

Thrown by the lockUnlock cleanup helper in s3/backend_state.go:223 when stateMgr.Unlock() fails during the new-workspace init path. The message intentionally includes the Lock ID and tells the user a force-unlock may be required. It fires on the cleanup branch after the main operation errored or finished.

Source

Thrown at internal/backend/remote-state/s3/backend_state.go:223

			exists = true
			break
		}
	}

	// We need to create the object so it's listed by States.
	if !exists {
		// take a lock on this state while we write it
		lockInfo := statemgr.NewLockInfo()
		lockInfo.Operation = "init"
		lockId, err := client.Lock(lockInfo)
		if err != nil {
			return nil, diags.Append(fmt.Errorf("failed to lock s3 state: %s", err))
		}

		// Local helper function so we can call it multiple places
		lockUnlock := func(parent error) error {
			if err := stateMgr.Unlock(lockId); err != nil {
				return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err)
			}
			return parent
		}

		// Grab the value
		// This is to ensure that no one beat us to writing a state between
		// the `exists` check and taking the lock.
		if err := stateMgr.RefreshState(); err != nil {
			err = lockUnlock(err)
			return nil, diags.Append(err)
		}

		// If we have no state, we have to create an empty state
		if v := stateMgr.State(); v == nil {
			if err := stateMgr.WriteState(states.NewState()); err != nil {
				err = lockUnlock(err)
				return nil, diags.Append(err)
			}

View on GitHub (pinned to c9def3e214)

Solutions

  1. Run `terraform force-unlock <Lock ID>` with the ID from the message.
  2. Manually delete the stale <key>.tflock S3 object and the DynamoDB LockID row.
  3. Verify the role has s3:DeleteObject and dynamodb:DeleteItem permissions.
  4. Retry init once the lock is cleared.
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify delete permission on the lock key before init
// _, err := s3Client.DeleteObject(ctx, &s3.DeleteObjectInput{Bucket:&bucket, Key:aws.String("probe")}) // sandboxed

Try / catch

// Unlock cleanup failure: warn and instruct force-unlock
// if err := stateMgr.Unlock(lockID); err != nil {
//   log.Warn("state may still be locked; run terraform force-unlock", "id", lockID, "err", err)
// }

Prevention

When it happens

Trigger: The lock was already released out-of-band; S3 DeleteObject of the .tflock failed (permissions/network); DynamoDB DeleteItem failed; the lock ID no longer matches the stored lock; connection/credentials dropped mid-cleanup.

Common situations: Interrupted init where the lock object was partially cleaned; another operator force-unlocked concurrently; IAM role lacks s3:DeleteObject on the lock key or dynamodb:DeleteItem.

Related errors


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