Salesforce Flow Error Emails: How to Read and Fix Them

By Prit Sakhvala, Salesforce Developer · Updated August 2026

"An unhandled fault has occurred in this flow" means a flow element failed with no fault path connected. The email goes (by default) to the admin who last modified the flow, names the failing element and error, and lists the interview's element-by-element path. To fix it: read the path, reproduce with a debug log, and add fault paths so failures are handled instead of crashing.

Anatomy of the error email

The email contains more diagnostic material than most people read:

The variable dump at the bottom is the part most people scroll past, and it frequently contains the entire answer. Scan it for variables holding null where you expected a record, collection variables with a count of zero, and Ids that look wrong for the object involved. A record variable showing null immediately before the failing element confirms the null-access diagnosis without needing a debug log at all.

One caution worth knowing: because these emails contain actual field values, they can carry customer data out to whoever receives them. On orgs handling regulated or personal data, route flow errors to a controlled internal address rather than personal inboxes, and treat the emails as production data rather than routine notifications.

What the user saw, and what actually happened

The email tells you a flow failed. It does not tell you what the failure did to your data, and that depends entirely on where the flow was running.

Flow typeWhat the user experiencedData impact
Record-triggered (after-save)Save appeared to fail, or succeeded then revertedTriggering transaction rolls back
Record-triggered (before-save)Save rejected with an errorNothing written
Screen flowGeneric "unhandled fault" message on screenRecords created earlier in the flow may persist
Scheduled flowNothing; no user presentThat batch fails silently; others continue
Autolaunched from ApexThe calling Apex throwsWhole Apex transaction rolls back

The screen flow row is the one that causes lasting damage. Because a screen flow spans multiple transactions (each screen commits before the next loads), a failure partway through can leave records created by earlier screens in place while later work never happens. The result is orphaned or half-built data that no error message mentions. When triaging a screen flow fault, always check what the earlier elements already wrote.

The scheduled flow row is the one people discover too late. Nobody is watching, so these failures accumulate in an inbox until someone notices the data is wrong. Scheduled flows are the strongest argument for logging faults to a custom object rather than relying on email.

Who gets the email, and how to change it

In Setup → Process Automation Settings, "Send Process or Flow Error Email to" offers:

OptionBehavior
User who last modified the process or flow (default)Only the last editor knows anything is failing; risky if they've left the team.
User who launched the flowEnd users see errors for flows they trigger; rarely what you want in production.
Apex exception email recipientsRecommended: the list in Setup → Apex Exception Email, which can include multiple admins and external addresses, giving one shared inbox for both Apex and Flow failures.

Triage: what to do in the first ten minutes

When the emails are arriving faster than you can read them, the instinct is to open one and start debugging. Stop the bleeding first: a failing record-triggered flow is rolling back real users' saves while you investigate.

  1. Establish the blast radius. Are these emails for one record or hundreds? One is a data problem; hundreds means every save on that object is failing and users are actively blocked.
  2. Check whether the flow version is new. The email names the flow version. If it matches something deployed today, you have your cause, and rolling back to the previous active version is faster than fixing forward.
  3. Decide whether to deactivate. For a widespread failure, deactivating the flow version restores service immediately. This is usually right for a flow doing non-critical work, and wrong for one enforcing data integrity, since deactivating it lets bad data through silently.
  4. Capture one good reproduction. Before changing anything, take one email's record Id and running user and reproduce with a debug log active. Once you deactivate or edit the flow, that evidence is gone.
  5. Only then diagnose. With the log captured and the bleeding stopped, work through the root-cause steps below without time pressure.

Step 4 is the one most often skipped and most regretted. A reproduction against the exact failing record, with the log captured, answers questions no amount of reading the flow in Flow Builder will, particularly when the cause is another automation rather than the flow itself.

If the failures began without any deployment, suspect the transaction rather than the flow. A new managed package, a new trigger, or simple data growth can push a transaction over a governor limit that the flow was previously fitting inside comfortably.

From email to root cause

  1. Read the element path bottom-up. The last element failed; the elements before it produced its inputs.
  2. Check the variable values. A null record variable before a DML or assignment element is the classic culprit (a Get Records found nothing; see Flow debugging for the fix pattern).
  3. Reproduce with a debug log. The email can't show why a DML failed or what other automation interfered. Activate a trace flag (or use ForceLens Smart Capture), repeat the action on the triggering record, and read the log: FLOW_ELEMENT_ERROR marks the failure, and the surrounding VALIDATION_RULE, CODE_UNIT_STARTED (triggers) and WF_RULE events expose interactions, like a validation rule on a related object blocking the flow's update, or a trigger consuming the SOQL budget before the flow ran (see order of execution).
  4. Check limits. If the message is a limit error, the log's LIMIT_USAGE_FOR_NS section shows what consumed the budget — often not the flow itself (governor limits explained).

Decoding the error message

The message in the email is the fastest route to a diagnosis, provided you know what each one usually means. These cover the overwhelming majority:

Message in the emailWhat actually happenedWhere to fix it
"The flow tried to access a null value"A Get Records found nothing; a later element read from the empty variableDecision check after the Get Records, not at the failing element
"FIELD_CUSTOM_VALIDATION_EXCEPTION"A validation rule rejected the flow's DMLThe validation rule, or the data the flow supplies
"REQUIRED_FIELD_MISSING"A Create Records element omitted a required fieldAdd the field, or supply a default
"Too many SOQL queries: 101"Get Records in a loop, or the transaction was already near its capMove the query out of the loop; check other automation
"UNABLE_TO_LOCK_ROW"Two transactions tried to update the same record simultaneouslyUsually a concurrency issue, not a flow bug; see below
"CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY"An Apex trigger on the target object threw during the flow's DMLThe trigger, not the flow; see below

Two of these deserve elaboration because they are routinely misdiagnosed.

Validation exceptions are not flow bugs. The flow did exactly what it was told; a rule elsewhere rejected the write. The email surfaces the validation rule's own error text, which is why reading the full message matters: it usually names the rule or the field. The decision to make is whether the flow should supply better data or the rule should exempt this case.

UNABLE_TO_LOCK_ROW is a timing problem. It means another transaction held a lock on the record your flow tried to update. This appears in orgs with heavy automation on parent objects — several child updates each triggering a parent roll-up at once. The flow is a victim rather than a cause, and the fix is usually to reduce contention by moving work asynchronous, not to change the flow.

CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY

The most misleading error in the list, because it names your flow while describing someone else's failure. CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY means the flow's DML reached the database, an Apex trigger on that object then ran, and the trigger threw. The flow is reported as the failure because it owned the transaction, but the bug is in the trigger.

The email usually carries the trigger's own message after the error code, in the form ObjectTrigger: execution of AfterUpdate caused by: System.…. That trailing exception is the real error, and the flow's element path will not explain it.

Spotting it in the log. This is where a debug log settles the argument, because the email cannot show what happened inside the trigger. Find the FLOW_ELEMENT_BEGIN for the failing element, then read forward: you will see DML_BEGIN, then CODE_UNIT_STARTED for the trigger, then the exception inside it.

10:31:02.118|FLOW_ELEMENT_BEGIN|[Update_Account] 10:31:02.140|DML_BEGIN|[EXTERNAL]|Op:Update|Type:Account|Rows:1 10:31:02.166|CODE_UNIT_STARTED|[EXTERNAL]|AccountTrigger on Account trigger event AfterUpdate 10:31:02.201|EXCEPTION_THROWN|[52]|System.NullPointerException: Attempt to de-reference a null object 10:31:02.203|FATAL_ERROR|System.DmlException: Update failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, AccountTrigger: execution of AfterUpdate

The EXCEPTION_THROWN at line 52 of the trigger is the actual cause. Everything after it is Salesforce reporting the consequence back up the chain to the flow.

Where to fix it. In the trigger, not the flow. That said, the flow can still be complicit: if it updates records the trigger was never designed to receive, or updates them in bulk when the trigger was written for one record, the trigger's assumption is what breaks. Trace the trigger's failure with the debug log, then decide which side should change.

Preventing the crash: fault paths

Every element that can fail (Get/Create/Update/Delete Records, actions, callouts, subflows) has a fault connector, and leaving it unconnected is what turns a recoverable condition into a crash. Connect it to handling logic and the fault becomes handled: no crash, no unhandled-fault email, and (if you choose) a graceful message or a logged error record. A pragmatic pattern:

Also add decision checks after every Get Records ("was anything found?"). It is the cheapest insurance against the null-value fault, which is the most common flow error of all.

Building the error log object

Email is a poor monitoring system: it goes to one person, gets filtered, and disappears when they leave. A custom logging object turns the same information into something reportable. A workable minimum:

FieldPopulate withWhy it matters
Flow Name (Text)Hard-coded per flowGroup failures by source
Error Message (Long Text){!$Flow.FaultMessage}The actual system error
Element Name (Text)Hard-coded on each fault pathLocates the failure without a log
Record Id (Text)The triggering recordYour reproduction recipe
Running User (Lookup){!$User.Id}Permission-related failures
Occurred At (DateTime){!$Flow.CurrentDateTime}Correlate with deployments and load

Setting the Element Name manually on each fault path feels tedious and repays the effort immediately: it is the difference between "this flow failed" and "the Update Contacts element failed", which often removes the need to capture a log at all.

One important caveat: in a record-triggered flow, the error record is written inside the same transaction that is about to roll back. If the transaction fails after your fault path runs, the log record disappears with everything else. To guarantee the record survives, write it from an asynchronous path or a subflow running in its own transaction. For screen flows this is not a concern, since each screen commits independently.

With that object in place, a simple report grouped by flow name and a dashboard component showing failures over the last seven days gives the whole team visibility that no inbox can. Add a scheduled report subscription and you have genuine monitoring rather than ad-hoc notification.

When you should not add a fault path

Fault paths are not universally correct. If a record-triggered flow performs work that must succeed for the data to be valid, catching the fault and continuing silently produces corrupted records, a considerably worse outcome than a visible failure and a rollback.

The test is simple: would you rather the save failed, or that it succeeded with incomplete data? If the answer is that it should fail, let the fault propagate and rely on the email. Use the fault path only to log the failure, then leave the flow to end in error rather than routing it to a "success" outcome.

Reviewing flows before they fail

Missing fault paths and unguarded Get Records are exactly the structural issues ForceLens's Flow analyzer flags: hand it any flow from Flow Builder and the Developer and QA lenses list unhandled fault connectors, null-risk references and bulkification concerns — with a health score and a ready .docx report. Cheaper than learning from production emails.

Frequently asked questions

Who receives flow error emails?

By default the admin who last modified the flow. Change it in Setup → Process Automation Settings: "Apex exception email recipients" is the most team-friendly option.

What does "An unhandled fault has occurred in this flow" mean?

An element failed with no fault path attached, crashing the interview and (for record-triggered flows) rolling back the triggering transaction. The email names the element and error and lists the executed path.

How do I find the root cause?

Read the element path and variable values in the email, then reproduce with a debug log; FLOW_ELEMENT_ERROR plus surrounding trigger/validation events show what the email can't.

How do I stop unhandled-fault emails?

Add fault paths to every fallible element. Handled faults don't send the email, and can log to a custom object instead for proper monitoring.

Why am I suddenly getting hundreds of flow error emails?

Usually a data load or bulk update hitting a flow that fails per record. Each failed interview sends its own email. Deactivate the flow version or pause the load first, then diagnose, and add a fault path so the next bulk operation logs rather than mails. If the failures started without a deployment, check whether another automation or package changed the transaction's limit consumption.

Does a flow error roll back the whole transaction?

It depends on the flow type. A record-triggered flow failing rolls back the triggering save. A screen flow is different: each screen commits separately, so records created by earlier screens can persist even though the flow failed later, leaving partial data that the error email does not mention.

Can I send flow error emails to more than one person?

Yes. In Setup → Process Automation Settings, set "Send Process or Flow Error Email to" to Apex exception email recipients, then maintain that list in Setup → Apex Exception Email. It accepts multiple addresses including external ones, so a shared team inbox receives both Apex and Flow failures.

Why does my error log record disappear after the flow fails?

Because in a record-triggered flow the fault path runs inside the transaction that is rolling back, so the log record is discarded with everything else. Write the error record from an asynchronous path or a subflow that runs in its own transaction to guarantee it survives.

The email says a validation rule failed, is that a flow bug?

Usually not. The flow performed its DML correctly and a validation rule rejected the write. The email includes the rule's own error text, which normally names the rule or field. Decide whether the flow should supply better data or the rule should exempt this case.

What does CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY mean in a flow error email?

An Apex trigger on the object your flow wrote to threw an exception. The flow is named because it owned the transaction, but the bug is in the trigger. The email usually appends the trigger's own message after the error code. In a debug log, read forward from FLOW_ELEMENT_BEGIN to the trigger's CODE_UNIT_STARTED: the EXCEPTION_THROWN inside it is the real cause.

Trace the failure in one click

Reproduce the error with ForceLens Smart Capture and the analyzed log opens itself: flow events, triggers, validation and limits, all in order. Free and local.

Add ForceLens to Chrome (Free)