SfSkills Domains Docs GitHub

Home / flow / flow-error-notification-patterns

flow-error-notification-patterns

Use when Flow fault emails are noisy, repeated, going to the wrong person, or need to go somewhere other than email.

Open the skill folder on GitHub


Flow Error Notification Patterns

Default Salesforce Flow error handling is a single config field: the "apex exception email recipient" set in Setup → Process Automation → Process Automation Settings. Every unhandled fault — across every flow in the org — ends up in that user's inbox as a plain-text dump of the flow interview state. For a small org with one admin, fine. For anything past that, it's the wrong shape: noisy, undifferentiated, slow-to-triage, with no per-flow severity or owner.

This skill is about replacing that default with a deliberate fault-path + notification design — what to alert on, who to alert, through which channel, and how to suppress expected rejections so they don't drown the real signal.

What this skill is NOT. The mechanic of adding a Fault connector to a Flow element is plain Flow Builder; consult Salesforce help. Apex trigger error handling is a different runtime — see apex/apex-exception-handling. This skill covers the design layer on top of those mechanics.


Before Starting


Core Concepts

What the default unhandled-fault email actually contains

When a Flow element fails and there's no Fault path, Salesforce sends a plain-text email to the configured "apex exception email recipient" containing:

This is debuggable but not actionable. There's no severity, no owner, no grouping by flow, no trend visibility. For org-wide scale it's a flood; for narrow problems it's invisible (a single error email gets lost in 50 unrelated ones).

Fault path semantics

Every flow element that can fail (Get Records, Create Records, Update Records, Action / callout, Delete Records, Loop with sub-elements) supports a Fault connector. The Fault path:

That last point is critical. A common mistake is "I added a fault path that does nothing" — yes, and now the platform thinks the flow succeeded, no email fires, and the failure is invisible.

$Flow.FaultMessage

Available inside any Fault path. Contains the error string from the failing element. Useful in screen flows to show the user what went wrong:

"We couldn't save your changes: {!$Flow.FaultMessage}"

In non-screen flows (record-triggered, scheduled, autolaunched), $Flow.FaultMessage is what you log to your notification channel. Without capturing it, the Fault path has no idea why the parent element failed.

Differentiating real errors from expected business rejections

A validation rule rejecting a DML inside a flow IS a fault from the flow's perspective. But it's not an error from the user's perspective — it's the system telling them their input is wrong. Treating both the same way means:

The fix is to differentiate inside the Fault path. The $Flow.FaultMessage string contains the validation rule's error message verbatim — you can detect "VALIDATION" / known business rejection patterns and route those to a user-facing display, while unknown errors go to the alert channel.


Common Patterns

Pattern A — Default-org behavior is wrong; replace with custom error log

When to use. Any org with more than one production flow.

Setup.

  1. Create a custom object Flow_Error_Log__c with fields: Flow_Name__c, Element__c, Fault_Message__c, Record_Id__c, User__c, Severity__c.
  2. Create a sub-flow Log_Flow_Error that takes those fields as input variables and creates a Flow_Error_Log__c record.
  3. In every production flow, add a Fault path on every fault-capable element. Each Fault path calls Log_Flow_Error with the element name, $Flow.FaultMessage, the record being processed, and a severity.
  4. Build a report on Flow_Error_Log__c filtered by recent + high-severity. Subscribe admins.

This is more setup than "set the org-default email recipient" but it gives:

Pattern B — Screen flow with user-visible error and silent admin notification

When to use. Screen flow where the user should see what went wrong (validation message, missing data) AND the admin should be notified asynchronously of programmer-error cases (failed callout, governor limit hit).

[Get Records / DML / Action]
    │
    ├── Success → next element
    │
    └── Fault → [Decision: is this a known business rejection?]
                    │
                    ├── Yes → [Display Text: "Couldn't save: {!$Flow.FaultMessage}"]
                    │           → Screen ends with user-visible error.
                    │
                    └── No (programmer error) →
                            ├── [Action: Publish Flow_Error_Event__e]
                            └── [Display Text: "An unexpected error occurred. Admins have been notified."]

The user sees a friendly "couldn't save" or a generic "we've been notified". The admin's monitoring subscriber catches the Platform Event for programmer-error cases only.

Pattern C — Record-triggered after-save: Platform Event for async notification

When to use. A record-triggered after-save flow that updates related records or publishes events; failure is silent to the user and must be caught.

Why Platform Event instead of direct email or Flow_Error_Log__c insert: a Platform Event is durable, triggers an Apex subscriber that can decide alert routing (email vs Slack vs PagerDuty vs queue), and keeps the flow's transaction clean — even if the alert delivery is slow, the flow doesn't wait.

Approach.

  1. Define Flow_Error_Event__e with fields Flow_Name__c, Element__c, Fault_Message__c, Record_Id__c.
  2. Apex subscriber on the Platform Event routes to the right channel based on Flow_Name__c.
  3. Every Fault path in record-triggered flows publishes the event.

The flow doesn't know or care whether the alert went to Slack, email, or both — that's the Apex subscriber's call.

Pattern D — Suppressing expected-rejection paths

When to use. A validation rule that rejects bad input is the intended behavior; without suppression, every rejection produces a fault-email or log entry, drowning real signal.

Approach. Inside the Fault path, decision element checks $Flow.FaultMessage against known business-rejection patterns (validation rule errors typically contain FIELD_CUSTOM_VALIDATION_EXCEPTION or the validation rule's error message verbatim). Match → display to user, do NOT log. No match → log + alert.

Fault → [Decision]
            │
            ├── $Flow.FaultMessage CONTAINS "FIELD_CUSTOM_VALIDATION_EXCEPTION"
            │       → [Display Text: "{!$Flow.FaultMessage}"]
            │       → screen ends; not logged
            │
            └── otherwise (programmer error)
                    → log to Flow_Error_Log__c + display generic message

The validation messages still reach the user as the user-friendly error; the alerting channel only fires on unknown errors.


Decision Guidance

SituationApproachReason
Org has only the org-default "apex exception email recipient"Replace with Pattern ADefault is a flood; custom log is queryable, groupable, retainable
Screen flow with mixed user error + programmer error potentialPattern B with decision branchUser sees actionable error; admin only alerted on programmer errors
Record-triggered after-save flow doing callouts / DMLPattern C with Platform EventAsync, durable, decouples flow from alert delivery
Scheduled flow that runs nightlyPattern A or C with severity = highNo user; silent failure is the failure mode
Autolaunched flow called from process-builder or other flowPattern A by default; C if high-volumeCaller's context decides whether user is in scope
Validation rule rejection is expectedPattern D suppression inside FaultDon't drown signal with expected-rejection noise
Sub-flow that's reused across many parent flowsSub-flow logs once with parent flow nameEach parent passes its name as input variable so logs are attributed correctly
Flow Orchestration runs (multi-stage)Same patterns; alert at stage boundaryStages can run for hours/days; per-step alerting is too noisy
Org has Slack / PagerDuty / observability toolingPlatform Event subscriber routesDon't hardcode the channel in the flow

  1. Inventory production flows that have no Fault paths. Default-behavior orgs have many.
  2. Build the Flow_Error_Log__c object + Log_Flow_Error sub-flow (Pattern A) once per org.
  3. For screen flows: add Fault paths with the decision branch from Pattern B.
  4. For record-triggered / scheduled / autolaunched: add Fault paths that log + (optionally) publish a Platform Event (Pattern C).
  5. Build the suppression list of expected-rejection patterns (Pattern D) in the decision element.
  6. Subscribe admins to a daily / hourly digest report on the error log object — not real-time on every error, which is its own flood.
  7. Audit periodically: pull the last 30 days of error log; identify flows with the most errors and address root causes.

Review Checklist


Salesforce-Specific Gotchas

  1. A "do-nothing" Fault path silently succeeds the flow. No email, no log, no signal — failure is invisible. (See references/gotchas.md § 1.)
  2. Default unhandled-fault email contains the entire interview state as plain text — easy hundreds of lines of irrelevant variable values. (See references/gotchas.md § 2.)
  3. $Flow.FaultMessage is empty in some contexts. The Fault path's surrounding element type matters. (See references/gotchas.md § 3.)
  4. Screen flow Fault paths that don't display anything to the user produce a blank screen. The user thinks the screen broke. (See references/gotchas.md § 4.)
  5. Record-triggered before-save flow Fault paths can't send email synchronously without governor limit risk. Use a Platform Event or Flow_Error_Log__c insert. (See references/gotchas.md § 5.)
  6. Send Email Action inside a Fault path can throw its own fault, which has no further fault path — the secondary failure is unhandled. (See references/gotchas.md § 6.)
  7. Validation rule errors and DML faults look identical to the platform. Differentiation requires inspecting the message string. (See references/gotchas.md § 7.)

Output Artifacts

ArtifactDescription
Flow_Error_Log__c custom object + fieldsSingle-org log target for non-screen-flow errors
Log_Flow_Error sub-flowReusable log-the-error step that every Fault path can invoke
Flow_Error_Event__e Platform Event + Apex subscriberAsync routing layer for orgs with multiple alert channels
Suppression-pattern listDocumented validation / business-rejection patterns that don't trigger alerts
Daily digest reportSubscribers see grouped error log, not per-event alerts

Package files