hashicorp/terraform · error
failed to unmarshal JSON data into LockInfo struct: %w
Error message
failed to unmarshal JSON data into LockInfo struct: %w
What it means
Thrown by unlockWithFile after the lock-file body is read but cannot be JSON-unmarshaled into statemgr.LockInfo. The .tflock file must contain a valid JSON document (Operation, Info, Who, Version, Created, Path, ID fields); corruption or a non-JSON file triggers this.
Source
Thrown at internal/backend/remote-state/s3/client.go:537
getOutput, err := c.s3Client.GetObject(ctx, getInput)
if err != nil {
return fmt.Errorf("unable to retrieve file from S3 bucket '%s' with key '%s': %w", c.bucketName, c.lockFilePath, err)
}
defer func() {
if cerr := getOutput.Body.Close(); cerr != nil {
log.Warn(fmt.Sprintf("failed to close S3 object body: %v", cerr))
}
}()
data, err := io.ReadAll(getOutput.Body)
if err != nil {
return fmt.Errorf("failed to read the body of the S3 object: %w", err)
}
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)
}
View on GitHub (pinned to c9def3e214)
Solutions
- Download the .tflock object and inspect its raw bytes to see what is actually stored.
- If the content is clearly garbage, back it up then delete it and re-run the unlock/apply.
- Pin all team members to a Terraform version that writes the same LockInfo schema.
- Disable any middleware (CDN, WAF, signed-URL proxy) that could rewrite the object.
Example fix
// before: lock file contains an error page // after: delete the corrupt file aws s3 rm s3://my-state-bucket/env:/prod/.tflock
Defensive patterns
Strategy: validation
Validate before calling
// Validate the lock file is valid LockInfo JSON before unlocking
func validateLockFile(ctx context.Context, s3c *s3.Client, bucket, lockKey string) error {
out, err := s3c.GetObject(ctx, &s3.GetObjectInput{Bucket: &bucket, Key: &lockKey})
if err != nil { return err }
defer out.Body.Close()
data, err := io.ReadAll(out.Body)
if err != nil { return err }
var li statemgr.LockInfo
if err := json.Unmarshal(data, &li); err != nil {
return fmt.Errorf("corrupt lock file (%w): %s", err, string(data))
}
return nil
} Try / catch
if err := client.Unlock(id); err != nil {
if strings.Contains(err.Error(), "failed to unmarshal JSON") {
// lock file corrupt; quarantine then delete
_, _ = s3c.CopyObject(ctx, &s3.CopyObjectInput{...}) // backup
_, _ = s3c.DeleteObject(ctx, &s3.DeleteObjectInput{Bucket: &bucket, Key: &lockKey})
}
} Prevention
- Validate lock-file JSON in a pre-flight step.
- Standardize the Terraform version writing locks.
- Block middleware (CDN/WAF) from rewriting lock-path objects.
- Back up a corrupt lock file before deleting so it is diagnosable.
When it happens
Trigger: Unlock -> unlockWithFile where json.Unmarshal(data, lockInfo) returns an error: file contains HTML/error page, partial JSON, an empty body, or a schema from a different/older Terraform version.
Common situations: A proxy or WAF replaced the object with an error page; the lock file was hand-edited or partially written due to a crashed previous run; an incompatible Terraform version wrote the file; or bucket versioning restored a corrupt older object.
Related errors
- failed to unmarshal JSON data into LockInfo struct: %w
- 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 read the body of the S3 object: %w
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/ce321d847162413b.
Report an issue: GitHub.