# Preventing action loops

Action loops occur when a task writes to a Shopify resource, triggering an update webhook that re-triggers the same task. For example, a task subscribed to `shopify/products/update` that adds a tag to a product will cause Shopify to fire another `shopify/products/update` event, creating a loop. This can lead to excessive API calls, duplicated data, or rate-limiting issues.

This guide covers strategies to prevent action loops in your Mechanic tasks.

### Manual Prevention

#### 1. Conditional Checks

#### **Example: Skip Tagging if Tag Already Exists**

{% code overflow="wrap" lineNumbers="true" fullWidth="false" %}

```liquid
{% if event.topic == "shopify/products/update" or event.preview %}
  {% assign existing_tags = product.tags | split: ", " %}
  
  {% unless existing_tags contains "NewTag" %}
    {% action "shopify" %}
      mutation {
        tagsAdd(
          id: {{ product.admin_graphql_api_id | json }},
          tags: ["NewTag"]
        ) {
          userErrors {
            field
            message
          }
        }
      }
    {% endaction %}
  {% else %}
    {% log "break loop" %}
  {% endunless %}
{% endif %}
```

{% endcode %}

#### 2. Timestamp-based Approach

**Example: Using Timestamps to Prevent Loops**

{% code overflow="wrap" lineNumbers="true" fullWidth="false" %}

```liquid
{% if event.topic == "shopify/products/update" or event.preview %}
  {% assign current_timestamp = "now" | date: "%s" %}
  {% assign time_difference = current_timestamp | minus: product.metafields.custom.last_updated.value %}
  
  {% if time_difference >= 500 %}
    {% action "shopify" %}
      mutation {
        productUpdate(
          input: {
            id: {{ product.admin_graphql_api_id | json }},
            metafields: [{
              id: {{ product.metafields.custom.last_updated.metafield.admin_graphql_api_id | json }},
              namespace: "custom",
              key: "last_updated",
              value: {{ current_timestamp | json }},
              type: "number_integer"
            }]
          }
        ) {
          userErrors {
            field
            message
          }
        }
      }
    {% endaction %}
    {% comment %}
      Do your update here
    {% endcomment %}
  {% else %}
    {% log "not so fast" %}
  {% endif %}
{% endif %}
```

{% endcode %}

### Automated Prevention

#### Mechanic's Built-in Features

Mechanic has some built-in features to prevent action loops:

1. For tasks responding to `mechanic/actions/perform`, Mechanic will detect identical results to their predecessors and mark the task run as failed.
2. For tasks responding to Shopify update events like `shopify/products/update`, Mechanic will detect repeated, identical task runs and error all action runs generated by the flagged task run.

## Related

* [Responding to action results](https://learn.mechanic.dev/techniques/responding-to-action-results) — how to safely subscribe to mechanic/actions/perform without creating loops
* [Debouncing events](https://learn.mechanic.dev/techniques/debouncing-events) — another approach to managing high-frequency events that could cause repeated processing
