hashicorp/terraform · error

failed to clean up file lock after DynamoDB lock error: %v;

Error message

failed to clean up file lock after DynamoDB lock error: %v; original error: %w

What it means

Thrown in RemoteClient.Lock() (s3/client.go:336) in the double-locking mode (useLockFile=true AND a dynamodb_table is configured). lockWithFile succeeded (S3 .tflock created) but lockWithDynamoDB failed, and the compensating unlockWithFile ALSO failed. The error bundles both: the cleanup failure (%v) and the original DynamoDB lock error (%w). A lock may be left in an inconsistent state.

Source

Thrown at internal/backend/remote-state/s3/client.go:336

		log.Info("Attempting to lock remote state (DynamoDB only)...")
		if err := c.lockWithDynamoDB(ctx, info); err != nil {
			return "", err
		}

		log.Info("Locked remote state (DynamoDB only)")
		return info.ID, nil
	}

	// double locking: dynamodb + file (design decision: both must succeed)
	log.Info("Attempting to lock remote state (S3 Native and DynamoDB)...")
	if err := c.lockWithFile(ctx, info, log); err != nil {
		return "", err
	}

	if err := c.lockWithDynamoDB(ctx, info); err != nil {
		// Release the file lock if attempting to acquire the DynamoDB lock fails.
		if unlockErr := c.unlockWithFile(ctx, info.ID, &statemgr.LockError{}, log); unlockErr != nil {
			return "", fmt.Errorf("failed to clean up file lock after DynamoDB lock error: %v; original error: %w", unlockErr, err)
		}

		return "", err
	}

	log.Info("Locked remote state (S3 Native and DynamoDB)")
	return info.ID, nil
}

// lockWithFile attempts to acquire a lock on the remote state by uploading a lock file to Amazon S3.
//
// This method is used when the S3 native locking mechanism is in use. It uploads a lock file (JSON)
// to an S3 bucket to establish a lock on the state file. If the lock file does not already
// exist, the operation will succeed, acquiring the lock. If the lock file already exists, the operation
// will fail due to a conditional write, indicating that the lock is already held by another Terraform client.
func (c *RemoteClient) lockWithFile(ctx context.Context, info *statemgr.LockInfo, log hclog.Logger) error {
	lockFileJson, err := json.Marshal(info)
	if err != nil {

View on GitHub (pinned to c9def3e214)

Solutions

  1. Manually delete the stale <key>.tflock object in S3 and the DynamoDB LockID row if one was created.
  2. Fix the dynamodb_table name/permissions and the S3 delete permission, then retry.
  3. Run `terraform force-unlock` if Terraform still reports the state as locked.
  4. Consider using a single locking mechanism (file-only or DynamoDB-only) to avoid the double-lock cleanup path.
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check both lock destinations are writable before locking
// _, ferr := s3Client.HeadObject(ctx, &s3.HeadObjectInput{Bucket:&bucket, Key:aws.String(lockFilePath)})
// _, derr := dynClient.DescribeTable(ctx, &dynamodb.DescribeTableInput{TableName:aws.String(ddbTable)})

Type guard

// The wrapped %w is the original DynamoDB lock error (often a *statemgr.LockError)
// var lockErr *statemgr.LockError
// if errors.As(err, &lockErr) { /* inspect lockErr.Info / Err */ }

Try / catch

// On double-lock cleanup failure, force manual cleanup rather than retrying blindly
// _, err := client.Lock(info)
// if err != nil && strings.Contains(err.Error(), "failed to clean up file lock") {
//   // delete stale .tflock + DynamoDB row, then force-unlock
// }

Prevention

When it happens

Trigger: S3 lock file created, then DynamoDB PutItem failed (table missing/misspelled, ConditionalCheckFailedException, dynamodb:PutItem denied), and the subsequent S3 delete of the .tflock also failed (s3:DeleteObject denied, network error).

Common situations: Misconfigured dynamodb_table name combined with insufficient S3 delete permissions; partial AWS outage; IAM policy allows PutObject but not DeleteObject on the lock key.

Related errors


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