{"record":{"id":"7983453f7cf0e22e","repo":"hashicorp/terraform","slug":"unable-to-retrieve-file-from-s3-bucket-s-with-k","errorCode":null,"errorMessage":"unable to retrieve file from S3 bucket '%s' with key '%s': %w","messagePattern":"unable to retrieve file from S3 bucket '(.+?)' with key '(.+?)': %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/s3/client.go","lineNumber":522,"sourceCode":"//\n// This method is used when the S3 native locking mechanism is in use, which uses a `.tflock` file\n// to manage state locking. The function deletes the lock file to release the lock, allowing other\n// Terraform clients to acquire the lock on the same state file.\nfunc (c *RemoteClient) unlockWithFile(ctx context.Context, id string, lockErr *statemgr.LockError, log hclog.Logger) error {\n\tgetInput := &s3.GetObjectInput{\n\t\tBucket: aws.String(c.bucketName),\n\t\tKey:    aws.String(c.lockFilePath),\n\t}\n\n\tif c.serverSideEncryption && c.customerEncryptionKey != nil {\n\t\tgetInput.SSECustomerKey = aws.String(base64.StdEncoding.EncodeToString(c.customerEncryptionKey))\n\t\tgetInput.SSECustomerAlgorithm = aws.String(s3EncryptionAlgorithm)\n\t\tgetInput.SSECustomerKeyMD5 = aws.String(c.getSSECustomerKeyMD5())\n\t}\n\n\tgetOutput, err := c.s3Client.GetObject(ctx, getInput)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to retrieve file from S3 bucket '%s' with key '%s': %w\", c.bucketName, c.lockFilePath, err)\n\t}\n\tdefer func() {\n\t\tif cerr := getOutput.Body.Close(); cerr != nil {\n\t\t\tlog.Warn(fmt.Sprintf(\"failed to close S3 object body: %v\", cerr))\n\t\t}\n\t}()\n\n\tdata, err := io.ReadAll(getOutput.Body)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read the body of the S3 object: %w\", err)\n\t}\n\n\tlockInfo := &statemgr.LockInfo{}\n\tif err := json.Unmarshal(data, lockInfo); err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal JSON data into LockInfo struct: %w\", err)\n\t}\n\tlockErr.Info = lockInfo\n","sourceCodeStart":504,"sourceCodeEnd":540,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/backend/remote-state/s3/client.go#L504-L540","documentation":"Thrown inside unlockWithFile when S3 GetObject on the lock file (.tflock) fails before the file can be read or deleted. The message reports bucket, key, and the underlying AWS error, so the operator can tell whether it is a permissions, missing-object, or connectivity problem. This blocks the S3 side of the unlock flow.","triggerScenarios":"Calling Unlock -> unlockWithFile where c.s3Client.GetObject returns an error for bucket c.bucketName and key c.lockFilePath: NoSuchKey, NoSuchBucket, AccessDenied, InvalidObjectName, or a transient networking/STS failure.","commonSituations":"Lock file already removed by a concurrent unlock or manual cleanup; bucket deleted or renamed; SSE-C key mismatch; wrong region configured for the bucket; or KMS key disabled so GetObject is denied.","solutions":["aws s3api get-object --bucket <bucket> --key <lockFilePath> to reproduce and read the exact AWS error code.","If NoSuchKey, the lock is effectively released; re-run the Terraform operation.","Confirm IAM s3:GetObject permission and the correct region/endpoint for the bucket.","For SSE-C, ensure the customer key matches; for SSE-KMS, ensure kms:Decrypt is granted."],"exampleFix":"// before: missing s3:GetObject on the lock path\n// after\n{\n  \"Effect\": \"Allow\",\n  \"Action\": \"s3:GetObject\",\n  \"Resource\": \"arn:aws:s3:::my-state-bucket/*.tflock\"\n}","handlingStrategy":"validation","validationCode":"// Head the lock file before attempting unlock to surface a clean error\nfunc lockFileExists(ctx context.Context, s3c *s3.Client, bucket, lockKey string) (bool, error) {\n  if _, err := s3c.HeadObject(ctx, &s3.HeadObjectInput{Bucket: &bucket, Key: &lockKey}); err != nil {\n    var nsk *types.NotFound\n    if errors.As(err, &nsk) || strings.Contains(err.Error(), \"NotFound\") {\n      return false, nil\n    }\n    return false, err\n  }\n  return true, nil\n}","typeGuard":null,"tryCatchPattern":"// Differentiate missing (benign) from real errors\nimport \"github.com/aws/smithy-go\"\n\nvar ae smithy.APIError\nif errors.As(err, &ae) && ae.ErrorCode() == \"NotFound\" {\n    // lock already gone\n} else {\n    // real failure, remediate\n}","preventionTips":["Pre-flight HeadObject on the .tflock key before unlock.","Keep IAM s3:GetObject granted on the lock prefix.","Avoid manual out-of-band deletes of the lock file.","Use consistent SSE configuration across all writers."],"tags":["s3","terraform-state","locking","iam","aws"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}