Skip to main content

Title: Mastering Salesforce Flow Fault Paths, Subflows & Invocable Apex: The Complete Guide

💬 In plain words: A Flow Fault Path functions as a built-in try/catch block. Without it, end users hit cryptic unhandled error screens and developers lose critical troubleshooting context. Subflows serve as modular, reusable functions. When declarative capabilities hit architectural boundaries, hand execution off to Apex using an @InvocableMethod.
📌 Example: Consider a refund Screen Flow that triggers an external payment gateway. Without a Fault Path, a gateway timeout presents an unformatted red error screen. With a configured Fault Path, the flow gracefully captures {!$Flow.FaultMessage}, logs the event to an Integration_Log__c record, notifies the user that the refund is queued, and routes the transaction for automated retry.

Architectural Concept

Production-grade flows require defensive architecture. Add a Fault Path to any declarative step prone to failure, including Get Records, Create Records, Update Records, Delete Records, and Core Actions.

  • Graceful Error Routing: Divert failed operations to custom handling paths to display clear user-facing messages or persist the system error message via {!$Flow.FaultMessage}.
  • Modular Architecture: Extract repeatable business logic into autolaunched subflows to avoid duplicate configurations and simplify ongoing maintenance.
  • Programmatic Extensibility: When business logic demands complex nested collections, precise callout retry algorithms, or extensive CPU-heavy computations, offload the workload to Apex using an @InvocableMethod.
  • Built-In Bulkification: Invocable Apex acts as a bulk-safe bridge, taking a List<Request> and returning a matching List<Result>.
  • Clean Separation of Concerns: Empower declarative builders to manage orchestration while delegating computationally intensive logic to programmatic code.
🧠 Core Rule: Attach a Fault Path to every DML and external action element. Standardize reusability via Subflows. Escalate complex logic to bulk-safe Invocable Apex using collections.
🧭 360 Card — Fault Paths, Subflows & Invocable Apex
  • Rule: Every element capable of throwing an unhandled exception must have a Fault Path. Repeated logic belongs inside a Subflow.
  • Gain: Total governance over the failure lifecycle—log errors, provide helpful user feedback, rethrow cleanly, or route tasks to recovery queues.
  • When to Use: Handle errors declaratively first. Introduce an @InvocableMethod when logic requires data transformations or API controls beyond Flow capabilities. Modularize shared steps into Subflows whenever logic appears in multiple places.
  • Price: Configuring fault routes across complex flows adds initial canvas overhead. However, omitting them exposes raw unhandled faults to end users.
  • Limits: Invocable methods receive and return lists. Salesforce invokes the method once per transaction (up to 200 records), meaning single-record SOQL queries or callouts inside a loop will quickly hit governor limits.
  • Handling vs. Silent Fails: Database transactions roll back regardless. With intentional fault routing, teams capture contextual diagnostic logs instead of leaving users with cryptic system emails.
  • Enterprise Logging: Connect Fault Paths directly to an enterprise logging framework or Platform Events to maintain unified monitoring.
  • Scale & Volume: An invocable method that is not designed for bulk processing might pass individual unit tests but fail during bulk data loads.

Core Implementation Q&A

Q: How do you implement robust Flow error handling, and when is the right time to transition to Apex?

🎯 Quick Answer: Configure a Fault Path from every DML and Action element to log the fault and notify stakeholders or display friendly messages. Delegate execution to Apex (@InvocableMethod) when dealing with complex data iterations, dynamic callout retries, or when full programmatic unit testing is required.

Implementation Strategy:

  • Connect the Fault connector of any data modification or external action element directly to custom handling logic.
  • In Screen Flows, surface {!$Flow.FaultMessage} inside a clear, styled screen element to guide the user.
  • In Record-Triggered Flows, route failures to create log records, fire Platform Events, or send real-time alerts.
  • Use the Custom Error element to display readable, field-level or page-level error banners while rolling back the database transaction.
  • Transition to Apex when business requirements outgrow declarative Flow capabilities, such as advanced collection sorting, asynchronous callout chains, or complex data structures.
  • Always bulkify invocable methods by processing inputs as lists rather than singular objects.

Pattern: Bulk-Safe Invocable Apex

// 1. Bulk-ready Apex class exposed to Flow Builder
public class OrderPricingAction {

    // 2. Annotation exposing the action in Flow
    @InvocableMethod(label='Calculate Order Pricing' description='Calls pricing engine to compute bulk order discounts' category='Order Management')
    public static List<PriceResult> calculatePricing(List<PriceRequest> requests) {
        List<PriceResult> results = new List<PriceResult>();

        // 3. Process requests in bulk - never run SOQL or DML inside a loop
        for (PriceRequest req : requests) {
            PriceResult res = new PriceResult();
            // Perform business logic or calculation
            res.calculatedAmount = req.baseAmount != null ? req.baseAmount * 0.90 : 0.0;
            results.add(res);
        }

        // Return a 1:1 matching list of results
        return results;
    }

    // 4. Input wrapper class with invocable variables
    public class PriceRequest {
        @InvocableVariable(label='Order ID' required=true)
        public Id orderId;

        @InvocableVariable(label='Base Amount' required=true)
        public Decimal baseAmount;
    }

    // Output wrapper class
    public class PriceResult {
        @InvocableVariable(label='Calculated Amount')
        public Decimal calculatedAmount;
    }
}

Scenario-Based Troubleshooting

Q1: A record-triggered flow fails in production, displaying an unhandled fault error to the user. What architecture was missed, and how should it be fixed?

  • Root Cause: The flow encountered an exception (such as a validation rule violation, missing record, or null pointer) on an element lacking a configured Fault Path.
  • Resolution: Attach fault connectors to all data-altering elements. Route the error to capture {!$Flow.FaultMessage} into a dedicated log object or an asynchronous Platform Event. Use a Custom Error element to explain the issue clearly to the end user.
  • Monitoring: Configure automated apex/flow exception alert emails to notify system administrators immediately upon failure.
⚠️ Common Trap: Never assume a declarative step will always succeed. Omitting Fault Paths leaves your application vulnerable to unexpected unhandled errors whenever underlying validation rules or required fields change.

Q2: An @InvocableMethod executes successfully for single records but throws governor limit exceptions during bulk operations (e.g., Data Loader imports). Why?

  • Root Cause: The underlying Apex method is not properly bulkified. Flow combines interview transactions into batches of up to 200 records and executes the invocable method once with the full list.
  • The Fix: Avoid issuing SOQL queries, DML operations, or callouts within loops inside the method. Use Set collections and Map lookups to handle data in batches.
  • Callout Limits: When invoking external APIs, ensure the batch respects the synchronous limit of 100 callouts per transaction, or delegate processing to asynchronous Queueable Apex.
💡 Comparison: Fault Paths vs. Letting Flows Fail
While both scenarios result in a database rollback, unhandled flows generate confusing error notices and frustrate users. Designing Fault Paths gives you complete control over log data capture, system telemetry, and the user experience.