Bypass Flag Lifecycle: Preventing Silent Automation Skips
Bypass flags are a common pattern in Salesforce automation. A checkbox field like Bypass_Record_Triggered_Flows__c is set to true before a bulk operation to prevent recursive or unwanted Flow executions, then set back to false afterward. The problem arises when the second half of that lifecycle -- resetting the flag -- is forgotten or fails silently. The result is records permanently stuck in a bypass state, with all downstream automations silently skipping them.
Why Bypass Flags Exist
When performing bulk operations such as cloning hundreds of records, inserting records via data loader, or running batch Apex, Record-Triggered Flows fire for every record. This can cause:
- Recursive execution: A Flow updates a record, which fires the same Flow again.
- Governor limit exhaustion: Hundreds of Flow executions consume CPU time, SOQL queries, and DML operations.
- Unintended side effects: Flows designed for single-user interactions produce incorrect results when fired in bulk.
The bypass flag pattern addresses this by adding a checkbox field to the object and including an entry condition in every Record-Triggered Flow:
<!-- Flow entry condition -->
<conditions>
<leftValueReference>$Record.Bypass_Record_Triggered_Flows__c</leftValueReference>
<operator>EqualTo</operator>
<rightValue>
<booleanValue>false</booleanValue>
</rightValue>
</conditions>
When the flag is true, the Flow's entry condition evaluates to false and the Flow does not execute.
The Lifecycle Problem
The antipattern looks like this in Apex:
// Step 1: Set bypass flag
List<OrderItem> items = [SELECT Id FROM OrderItem WHERE OrderId = :orderId];
for (OrderItem item : items) {
item.Bypass_Record_Triggered_Flows__c = true;
}
update items;
// Step 2: Perform bulk operation
List<OrderItem> clonedItems = cloneOrderItems(items);
insert clonedItems;
// Step 3: MISSING -- bypass flag is never reset to false
// The original items now have Bypass = true permanently
After this code executes, every OrderItem on that Order has Bypass_Record_Triggered_Flows__c = true. Any future update to those records -- whether by a user, another Flow, or an integration -- will skip all Record-Triggered Flows. Forever.
Symptoms
The symptoms are insidious because they appear gradually and inconsistently:
- Automations work for newly created records but not for records that existed before a certain date.
- A specific set of records consistently fails to trigger expected automation.
- Users report that "the Flow stopped working" but only for certain records.
- Debugging the Flow shows it never enters execution -- the entry condition blocks it.
The Fix: Always Pair Set-True with Set-False
// Step 1: Set bypass flag
List<OrderItem> items = [SELECT Id, Bypass_Record_Triggered_Flows__c
FROM OrderItem WHERE OrderId = :orderId];
for (OrderItem item : items) {
item.Bypass_Record_Triggered_Flows__c = true;
}
update items;
// Step 2: Perform bulk operation
List<OrderItem> clonedItems = cloneOrderItems(items);
insert clonedItems;
// Step 3: Reset bypass flag immediately after
for (OrderItem item : items) {
item.Bypass_Record_Triggered_Flows__c = false;
}
update items;
// Step 4: Also reset on cloned records
for (OrderItem cloned : clonedItems) {
cloned.Bypass_Record_Triggered_Flows__c = false;
}
update clonedItems;
Using a Try-Finally Block
For maximum safety, wrap the operation in a try-finally block so the flag resets even if an exception occurs:
try {
// Set bypass
for (OrderItem item : items) {
item.Bypass_Record_Triggered_Flows__c = true;
}
update items;
// Perform operation
insert cloneOrderItems(items);
} finally {
// Always reset bypass, even on failure
for (OrderItem item : items) {
item.Bypass_Record_Triggered_Flows__c = false;
}
update items;
}
Best Practices for Bypass Flag Management
-
Always pair set-true with set-false in the same method or transaction. Treat bypass flag management like resource cleanup -- every open must have a close.
-
Use try-finally blocks to ensure the flag resets even when exceptions occur mid-operation.
-
Reset flags on cloned records too. If you clone records while the bypass flag is true, the cloned records inherit the flag value. Explicitly set them to false after insertion.
-
Audit for stale bypass flags. Run periodic SOQL queries to identify records with bypass flags stuck at true:
SELECT Id, Name, Bypass_Record_Triggered_Flows__c
FROM OrderItem
WHERE Bypass_Record_Triggered_Flows__c = true
-
Consider Queueable Apex for the reset. If DML limits are a concern, enqueue a Queueable job to reset the flags asynchronously. This guarantees the reset happens even if the synchronous transaction hits limits.
-
Document bypass flag usage. Every class or Flow that sets a bypass flag should have a comment or description noting where and when the flag is reset.
Key Takeaway
A bypass flag without a reset is a ticking time bomb. The automation skip is silent -- no errors, no logs, no alerts. Always treat bypass flags as a paired operation: every true must be followed by a false, ideally within the same transaction and protected by a try-finally block. Periodically auditing for stale bypass flags prevents a class of bugs that is notoriously difficult to diagnose after the fact.