hashicorp/terraform · error · statemgr.LockError
failed to unlock S3: %v
Error message
failed to unlock S3: %v
What it means
Returned by RemoteClient.Unlock in dual-locking mode when only the S3 lock-file deletion failed while the DynamoDB delete succeeded. It wraps the original error from unlockWithFile, which itself may be a GetObject, read, unmarshal, ID-mismatch, or DeleteObject failure. Because DynamoDB unlocked cleanly, only the S3 side needs remediation.
Source
Thrown at internal/backend/remote-state/s3/client.go:490
}
log.Info("Unlocked remote state (DynamoDB only)")
return nil
}
// Double unlocking: DynamoDB + file
log.Info("Attempting to unlock remote state (S3 Native and DynamoDB)...")
ferr := c.unlockWithFile(ctx, id, lockErr, log)
derr := c.unlockWithDynamoDB(ctx, id, lockErr)
if ferr != nil && derr != nil {
lockErr.Err = fmt.Errorf("failed to unlock both S3 and DynamoDB: S3 error: %v, DynamoDB error: %v", ferr, derr)
return lockErr
}
if ferr != nil {
lockErr.Err = fmt.Errorf("failed to unlock S3: %v", ferr)
return lockErr
}
if derr != nil {
lockErr.Err = fmt.Errorf("failed to unlock DynamoDB: %v", derr)
return lockErr
}
log.Info("Unlocked remote state (S3 Native and DynamoDB)")
return nil
}
// unlockWithFile attempts to unlock the remote state by deleting the lock file from Amazon S3.
//
// This method is used when the S3 native locking mechanism is in use, which uses a `.tflock` file
// to manage state locking. The function deletes the lock file to release the lock, allowing other
// Terraform clients to acquire the lock on the same state file.
func (c *RemoteClient) unlockWithFile(ctx context.Context, id string, lockErr *statemgr.LockError, log hclog.Logger) error {View on GitHub (pinned to c9def3e214)
Solutions
- Read the wrapped S3 error code: NoSuchKey means the file is already gone (safe to ignore), AccessDenied points to IAM/bucket policy.
- Confirm the S3 IAM principal has s3:GetObject and s3:DeleteObject on arn:aws:s3:::<bucket>/<lockFilePath>.
- If SSE-C is configured, verify the customer key provided to the backend matches the one used to create the lock file.
- Manually delete the orphaned .tflock object if the auto-delete keeps failing, then re-run the operation.
Example fix
// before: bucket policy denies DeleteObject on *.tflock
// after
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::my-state-bucket/*.tflock"
} Defensive patterns
Strategy: try-catch
Validate before calling
// Confirm s3:DeleteObject + GetObject on the lock key before unlock
func canDeleteLockFile(ctx context.Context, s3c *s3.Client, bucket, lockKey string) error {
if _, err := s3c.GetObject(ctx, &s3.GetObjectInput{Bucket: &bucket, Key: &lockKey}); err != nil {
return fmt.Errorf("cannot read lock file: %w", err)
}
return nil // DeleteObject grant verified by IAM policy review, not API
} Try / catch
err := client.Unlock(id)
if le, ok := err.(*statemgr.LockError); ok && strings.Contains(le.Err.Error(), "failed to unlock S3") {
// only S3 failed; DynamoDB is clean, retry S3 delete or remediate IAM
} Prevention
- Grant s3:GetObject + s3:DeleteObject on the *.tflock prefix in the runner role.
- Avoid S3 Object Lock (WORM) on the state bucket, which blocks lock-file deletes.
- Watch for bucket policies that explicitly Deny DeleteObject.
- Use SSE-S or correct SSE-C keys consistently to avoid GetObject denial.
When it happens
Trigger: Unlock(id) with useLockFile=true && ddbTable set, where DynamoDB DeleteItem succeeds but S3 GetObject or DeleteObject on the .tflock file returns an error (NoSuchKey, AccessDenied, KMS/MFA mismatch, or a stale lock ID that no longer matches the file contents).
Common situations: The .tflock object was already deleted out-of-band (manual cleanup) so GetObject fails; the S3 bucket policy changed; SSE-C customer key no longer matches; or another process raced to delete the lock file first.
Related errors
- failed to unlock both S3 and DynamoDB: S3 error: %v, DynamoD
- unable to retrieve file from S3 bucket '%s' with key '%s': %
- failed to delete the lock file: %w
- failed to unlock DynamoDB: %v
- failed to retrieve lock info for lock ID %q: %s
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/2b3e9bd1b858515b.
Report an issue: GitHub.