hashicorp/terraform · error
failed to delete the lock file: %w
Error message
failed to delete the lock file: %w
What it means
Thrown by unlockWithFile after the lock ID is verified and the code calls s3Client.DeleteObject on the .tflock key but AWS returns an error. The lock was successfully validated but could not be removed, so the lock effectively remains.
Source
Thrown at internal/backend/remote-state/s3/client.go:553
lockInfo := &statemgr.LockInfo{}
if err := json.Unmarshal(data, lockInfo); err != nil {
return fmt.Errorf("failed to unmarshal JSON data into LockInfo struct: %w", err)
}
lockErr.Info = lockInfo
// Verify that the provided lock ID matches the lock ID of the retrieved lock file.
if lockInfo.ID != id {
return fmt.Errorf("lock ID '%s' does not match the existing lock ID '%s'", id, lockInfo.ID)
}
// Delete the lock file to release the lock.
_, err = c.s3Client.DeleteObject(ctx, &s3.DeleteObjectInput{
Bucket: aws.String(c.bucketName),
Key: aws.String(c.lockFilePath),
})
if err != nil {
return fmt.Errorf("failed to delete the lock file: %w", err)
}
log.Debug(fmt.Sprintf("Deleted lock file: '%q'", c.lockFilePath))
return nil
}
func (c *RemoteClient) unlockWithDynamoDB(ctx context.Context, id string, lockErr *statemgr.LockError) error {
// TODO: store the path and lock ID in separate fields, and have proper
// projection expression only delete the lock if both match, rather than
// checking the ID from the info field first.
lockInfo, err := c.getLockInfoWithDynamoDB(ctx)
if err != nil {
return fmt.Errorf("failed to retrieve lock info for lock ID %q: %s", id, err)
}
lockErr.Info = lockInfo
if lockInfo.ID != id {View on GitHub (pinned to c9def3e214)
Solutions
- Confirm s3:DeleteObject permission on arn:aws:s3:::<bucket>/<lockFilePath>.
- Check the bucket is not in S3 Object Lock / WORM mode that blocks deletes.
- Review bucket policies and SCPs for explicit Deny on DeleteObject.
- As a last resort, use lifecycle rules or versioning suspend to release the lock, or delete via a privileged account.
Example fix
// before: only GetObject granted
// after
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::my-state-bucket/*"
} Defensive patterns
Strategy: validation
Validate before calling
// IAM-side check: ensure DeleteObject is granted (simulate or use IAM evaluator)
// Runtime pre-flight: try deleting a no-op test object under the same prefix
func canDeleteInPrefix(ctx context.Context, s3c *s3.Client, bucket string) error {
probe := "tflock-probe-" + uuid.NewString()
if _, err := s3c.PutObject(ctx, &s3.PutObjectInput{Bucket: &bucket, Key: &probe, Body: strings.NewReader("")}); err != nil { return err }
_, err := s3c.DeleteObject(ctx, &s3.DeleteObjectInput{Bucket: &bucket, Key: &probe})
return err
} Try / catch
if err := client.Unlock(id); err != nil {
var ae smithy.APIError
if errors.As(err, &ae) && (ae.ErrorCode()=="AccessDenied" || ae.ErrorCode()=="AccessDeniedException") {
// surface IAM remediation guidance
}
} Prevention
- Grant s3:DeleteObject on the state bucket prefix in the runner role.
- Avoid S3 Object Lock / WORM on the state bucket.
- Review bucket policies and SCPs for explicit Deny on deletes.
- Test delete capability with a probe object in CI setup.
When it happens
Trigger: Unlock -> unlockWithFile where c.s3Client.DeleteObject fails on the verified lock file: AccessDenied, bucket policy Deny, object in Glacier deep archive, KMS denial, or a transient AWS error.
Common situations: IAM principal has GetObject but not DeleteObject; a bucket policy explicitly denies deletes; object-locked bucket (WORM) prevents deletion; region mismatch; or a service control policy blocking deletes.
Related errors
- failed to unlock both S3 and DynamoDB: S3 error: %v, DynamoD
- failed to unlock S3: %v
- unable to retrieve file from S3 bucket '%s' with key '%s': %
- 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/d94905ca8cab1e4e.
Report an issue: GitHub.