Automations
Loop Protection

Loop Protection

Because automation actions can cascade — adding a tag fires a tag_applied trigger, which can add another tag, and so on — Gordon CRM includes built-in safety mechanisms to prevent infinite loops.

The Problem

Consider this set of rules:

RuleTriggerAction
Rule ATag "Lead" appliedAdd tag "Prospect"
Rule BTag "Prospect" appliedAdd tag "Lead"

Without protection, this would create an infinite loop:

Tag "Lead" applied
  → Add "Prospect"
    → Tag "Prospect" applied
      → Add "Lead"
        → Tag "Lead" applied
          → Add "Prospect"
            → … (forever)

Gordon CRM prevents this with two complementary strategies.


Strategy 1: Depth Limiter

The processAutomationTrigger function accepts an internal depth parameter (default 0). Every time an action causes a cascading trigger, the depth is incremented:

processAutomationTrigger(…, depth = 0)     ← Original trigger
  → add_tag action → fire tag_applied
     processAutomationTrigger(…, depth = 1)  ← First cascade
       → add_tag action → fire tag_applied
          processAutomationTrigger(…, depth = 2)  ← Second cascade
            → add_tag action → fire tag_applied
               processAutomationTrigger(…, depth = 3)  ← Third cascade
                 → add_tag action → fire tag_applied
                    processAutomationTrigger(…, depth = 4)  ← ABORT

The hard limit is depth > 3. When the depth exceeds 3, the engine stops additional cascading to safeguard system stability.

This limit allows for legitimate chaining (up to 3 levels deep) while preventing runaway loops.

Depth-Limit Notifications

When the depth limit is hit, Gordon CRM creates a notification in the Notification Center so you have visibility into what happened. The notification includes enriched details identifying the specific trigger and the blocked rule:

Automation chain depth limit reached The automation engine stopped after 4 chain steps to prevent an infinite loop. Trigger: tag applied "Pong". Blocked automation: "When tag Pong added → Add tag Ping".

If multiple contacts hit the same chain, notifications are grouped into a single entry with a count (e.g., "×50") rather than creating separate alerts. See Notification Center → Automation Depth Limit for full details on grouping and interaction.

What To Do

A depth-limit notification usually means you have a circular tag automation chain — two or more rules that trigger each other in a loop. To resolve it:

  1. Click the "View Automations" link in the notification to go directly to the Automations page.
  2. Look for tag-based rules that form a cycle (e.g., "Tag A applied → Add Tag B" paired with "Tag B applied → Add Tag A").
  3. Remove or deactivate one side of the cycle.
  4. Check the Dependency Tracking cards on your Tags page to see the full picture of which tags trigger which automations.

Strategy 2: Idempotent Operations

Even without the depth limiter, the tag operations themselves provide a natural circuit breaker:

Add Tag (Idempotent)

The add_tag action uses an upsert with ON CONFLICT (contact_id, tag_id) DO NOTHING:

INSERT INTO contact_tags (contact_id, tag_id)
VALUES ($1, $2)
ON CONFLICT (contact_id, tag_id) DO NOTHING;

If the contact already has the tag, the operation succeeds silently but no row is changed. The engine checks the result — if no rows were affected (the tag was already present), it does not fire the cascading tag_applied trigger. This naturally breaks the loop because the duplicate tag application produces no side effect.

Remove Tag (Idempotent)

The remove_tag action deletes the row and checks the SELECT return value. If no row was deleted (the tag was already absent), the cascading tag_removed trigger is not fired.


How The Two Strategies Work Together

ScenarioStrategy 1 (Depth)Strategy 2 (Idempotency)
A → B → A (circular)Would stop at depth 4Stops earlier — A is already applied, so the second application is a no-op
A → B → C → D → E (long chain)Stops at depth 4Does not apply — each tag is different
A → A (self-referencing)Would stop at depth 4Stops immediately — A is already applied

In practice, idempotency catches most circular loops before the depth limiter kicks in. The depth limiter acts as a safety net for edge cases where a long chain of different tags might trigger each other in sequence.


Campaign Enrollment Protection

The enroll_campaign action has its own independent idempotency check:

  • If the contact already has an active or processing enrollment → skip (no duplicate enrollment)
  • If a triggerEventId is present and a completed enrollment exists for the same trigger event → skip (prevents webhook retries from re-enrolling)

These checks prevent a scenario where cascading tag automations could enroll the same contact into a campaign multiple times.


Best Practices

  1. Keep chains short. Design your automations to resolve within 1–2 cascade levels when possible.
  2. Avoid circular tag dependencies. If "Tag A applied → Add Tag B" exists, avoid creating "Tag B applied → Add Tag A".
  3. Use campaign enrollment wisely. Campaign enrollment is naturally idempotent, but long chains that lead to enrollment still consume processing time.
  4. Watch for depth-limit notifications. If the automation engine hits the depth limit, a notification appears in the Notification Center. This is the clearest signal that your automation chain is too long or contains a circular dependency — the notification body identifies the specific trigger and blocked rule.