# 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](/techniques/responding-to-action-results.md) — how to safely subscribe to mechanic/actions/perform without creating loops
* [Debouncing events](/techniques/debouncing-events.md) — another approach to managing high-frequency events that could cause repeated processing


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://learn.mechanic.dev/techniques/preventing-action-loops.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
