hashicorp/terraform · error

failed to retrieve lock info for lock ID %q: %s

Error message

failed to retrieve lock info for lock ID %q: %s

What it means

Returned by unlockWithDynamoDB when getLockInfoWithDynamoDB fails (GetItem error or JSON unmarshal failure of the Info attribute). The message includes the attempted lock id and the wrapped retrieval error. This blocks the DynamoDB-side unlock because the code must first read the stored lock to verify ownership.

Source

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

		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 {
		return fmt.Errorf("lock ID %q does not match existing lock (%q)", id, lockInfo.ID)
	}

	params := &dynamodb.DeleteItemInput{
		Key: map[string]dynamodbtypes.AttributeValue{
			"LockID": &dynamodbtypes.AttributeValueMemberS{
				Value: c.lockPath(),
			},
		},
		TableName: aws.String(c.ddbTable),
	}
	_, err = c.dynClient.DeleteItem(ctx, params)

	if err != nil {

View on GitHub (pinned to c9def3e214)

Solutions

  1. Confirm dynamodb:GetItem permission and that the table name in the backend config matches the live table.
  2. If the table is gone, recreate it (or remove dynamodb_table from the config if migrating to S3-native locking).
  3. Inspect the row's Info attribute: aws dynamodb get-item --table-name <tbl> --key '{...}' --projection-expression Info.
  4. Repair or overwrite the Info JSON if it is malformed.

Example fix

# before: malformed Info attribute
# after: write valid LockInfo JSON
aws dynamodb put-item --table-name locks \
  --item '{"LockID":{"S":"<bucket>/<path>"},"Info":{"S":"{\"ID\":\"x\",\"Operation\":\"OperationTypeInvalid\",\"Who\":\"ci\",\"Version\":\"1.0\",\"Created\":\"2024-01-01T00:00:00Z\",\"Path\":\"<bucket>/<path>\"}"}}'
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight: table reachable and Info attribute is valid JSON
func validateDynamoLock(ctx context.Context, ddbs *dynamodb.Client, table, lockID string) error {
  if _, err := ddbs.DescribeTable(ctx, &dynamodb.DescribeTableInput{TableName: &table}); err != nil {
    return fmt.Errorf("table %q: %w", table, err)
  }
  return nil
}

Try / catch

if err := client.Unlock(id); err != nil {
    if strings.Contains(err.Error(), "failed to retrieve lock info") {
        // DDB read failed; check table existence and IAM before retry
    }
}

Prevention

When it happens

Trigger: Unlock -> unlockWithDynamoDB -> getLockInfoWithDynamoDB returns an error: DynamoDB GetItem fails (table missing, AccessDenied, throttling) or the Info attribute contains malformed JSON.

Common situations: Lock table dropped or renamed; IAM lacks dynamodb:GetItem; Info attribute corrupted by an older/incompatible Terraform or by a manual edit; throttling on a small provisioned table.

Related errors


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