SfSkills Domains Docs GitHub

Home / flow / flow-rollback-patterns

flow-rollback-patterns

Use the Flow Rollback Records element to undo DML inside the current transaction. Covers when rollback is appropriate vs catastrophic, how to combine with fault paths, partial-commit pitfalls, and the interaction with publish-after-commit. NOT for external-system rollback (use compensation patterns) — use apex/apex-savepoint-and-rollback.

Open the skill folder on GitHub


Flow Rollback Patterns

Core concept — rollback is transaction-scoped

The Flow Rollback Records element undoes DML that happened in the CURRENT transaction. It does not:

What gets rolled back

What does NOT get rolled back

  1. Map the DML sequence. List every Create / Update / Delete in the flow, in order.
  2. Identify failure modes that should trigger all-or-nothing rollback. Not every failure wants rollback — a logging failure shouldn't roll back the business work.
  3. Place Rollback Records in the fault path of the element where the critical-failure could occur. Don't rollback preemptively.
  4. Combine with logging. Rollback erases the records but not the audit trail — log to Integration_Log__c or similar BEFORE the rollback so you can forensically reconstruct what happened.
  5. Audit publish-after-commit events. If the flow publishes Platform Events with Publish Immediately, rollback doesn't retract them. Switch to Publish After Commit or accept the phantom-event risk.
  6. Test the rollback path. Force a fault in test and verify records are absent.

Key patterns

Pattern 1 — Create + Create + rollback-on-second-failure

[Get Account]
     │
     ▼
[Create Opportunity]    ────── fault path ──────►  [Rollback Records] → [End]
     │
     ▼
[Create OpportunityLineItem]  ─── fault path ────►  [Log to Integration_Log__c]
                                                          │
                                                          ▼
                                                    [Rollback Records]
                                                          │
                                                          ▼
                                                    [Send Email Alert]
                                                          │
                                                          ▼
                                                          [End]
     │
     ▼
[End] (success — both commits)

Design notes:

Pattern 2 — Screen flow cancel

Screen 1: Gather inputs
Screen 2: [Create Records — Case]  ──►  session.caseId = {!Case.Id}
Screen 3: [Create Records — Task]   ──►  session.taskId = {!Task.Id}
Screen 4: "Confirm submit?"  ──►  [Yes] → End
                                   [No]  → [Rollback Records] → [End]

If the user hits "No" at the confirm step, both created records get undone. This is superior to "Delete Records" because it handles compound writes atomically — deletes would need separate DML statements with their own failure modes.

Pattern 3 — Rollback with compensating Platform Event

When a Platform Event is published Publish Immediately and downstream subscribers have already acted, rolling back the local DML creates an inconsistency.

[Create Order]
     │
     ▼
[Publish Order_Created__e: Publish Immediately]  ◄── external billing system already charged
     │
     ▼
[Update Account — fault]
     │
     fault path
     ▼
[Rollback Records]      ◄── undoes Order + Account
[Publish Order_Cancelled__e] ◄── tell subscribers to undo their side

Publish-After-Commit would be better, but sometimes business constraints prevent the change. A compensating event is the recovery pattern.

Pattern 4 — Don't rollback on logging failure

[Business DML: Create + Update] ─── success ─►
     │
     ▼
[Log success to Integration_Log__c]  ─── fault path ─►  [Silent End]

A logging failure should NOT trigger rollback of the business work. The Silent End on the fault path is deliberate — the business work committed, the log failed, an admin can reconstruct from system logs later.

Bulk safety

Error handling

Well-Architected mapping

Gotchas

See references/gotchas.md.

Testing

In Apex test classes that invoke the flow:

@IsTest
static void testRollbackOnSecondDMLFailure() {
    // Setup: trigger a condition that makes the 2nd DML fail.
    Account a = new Account(Name='Test');
    insert a;
    // Pre-create a record that will collide with the flow's 2nd DML.
    ...

    Test.startTest();
    // Invoke flow; expect the whole transaction to rollback.
    try {
        Flow.Interview.MyFlow flow =
            new Flow.Interview.MyFlow(new Map<String, Object>{'accountId' => a.Id});
        flow.start();
    } catch (Exception e) {
        // Expected.
    }
    Test.stopTest();

    // Assert no Opportunity was created.
    System.assertEquals(0, [SELECT COUNT() FROM Opportunity WHERE AccountId = :a.Id]);
}

Official Sources Used

Package files