Salesforce Dictionary - Free Salesforce GlossarySalesforce Dictionary
All errors
Flow

An unhandled fault has occurred in this flow

A Flow ran into an error somewhere mid-run and had nowhere to go. By default Salesforce sends the unhandled-fault email to the running user (or the flow owner) with a stack trace. The fix is almost always to add a Fault path on the failing element so the flow handles its own errors gracefully.

Also seen asAn unhandled fault has occurred in this flow·An unhandled fault has occurred·Flow Error: An unhandled fault has occurred

A sales ops admin built a Screen Flow that creates an Opportunity, attaches a few related contacts, and posts a chatter update on the parent Account. It worked in the builder. It worked in debug. The first rep ran it in production, clicked Next on the contact screen, and got a red banner: "An unhandled fault has occurred in this flow." No record was created. No chatter post fired. Just a generic message and a flow that won't move forward.

What the platform is actually telling you

A flow runs as a transaction. Every element (Create Records, Update Records, Get Records, Action) executes inside that transaction. If any element throws an exception the runtime cannot recover from, the flow halts. When there's no Fault path wired up to that element, the runtime returns control to the user with the generic banner above, rolls back any DML the flow performed, and writes the underlying exception to the debug logs.

The banner is a placeholder. The real information lives in two places: the flow debug log and the Apex log generated by the transaction. Neither is visible to the end user. Without a Fault path, the platform has no way to surface the actual cause inline, so it falls back to the safest possible message.

The most common underlying causes are validation rule failures on Create or Update, missing required fields, governor limits exceeded, locked records, FLS denials for the running user, and Apex actions that throw uncaught exceptions. Each surfaces the same generic banner. Each requires different debugging.

The broken example

A Screen Flow named "New Opportunity From Account" takes an Account id, asks the rep for an opportunity name and amount, then runs a Create Records element on Opportunity:

Element: Create Opportunity
Object: Opportunity
Field Assignments:
  Name = {!opportunityName}
  AccountId = {!recordId}
  Amount = {!amountInput}
  StageName = 'Prospecting'

In a sandbox where the only validation rule is "Amount must be positive," everything works. In production, the Sales team added a validation rule six months ago: "If StageName is Prospecting, CloseDate must be within 90 days." The flow never sets CloseDate. The platform applies the field-level default if one is configured, but no default exists. The Create fails with a validation rule violation. The flow has no Fault path. The user sees the banner.

A second shape of the same defect: a Get Records element that filters by a custom field and returns zero rows, followed by an Assignment element that reads {!recordsCollection[0].Industry}. The collection is empty, the indexer fails, the flow halts. The banner is identical.

A third shape: an Apex action invoked from the flow throws an uncaught exception. The flow's view of the world is "the action failed" with no further detail. The actual exception lives in the Apex log. The banner is the same.

Why the message is so generic

The banner is a security feature, not a user-experience oversight. Flow runs as the calling user but can include element calls that touch records the user can see only indirectly. Surfacing the raw exception text in the UI could leak field names, validation rule logic, or relationship structures the user isn't authorized to read. The platform errs on the side of saying nothing and writing everything to the log.

For admins building flows, this means two things. First, you cannot debug the message itself; you have to read the log. Second, every flow you ship to end users needs a Fault path that surfaces a friendly, accurate explanation, because the default message is useless to anyone who didn't write the flow.

The fix: add Fault connectors and a screen to display them

Every flow element that can throw (Create, Update, Delete, Get, Action) supports a Fault outcome. Drag a connector from the element to a Screen, set the screen's content to display the exception's text, and the user sees what actually went wrong.

In Flow Builder, the Fault connector is a second arrow leaving the element, colored red. Wire it to an Error Screen that displays text like:

Something went wrong while creating the opportunity.

Details: {!$Flow.FaultMessage}

If this persists, contact your administrator.

$Flow.FaultMessage is a runtime variable that holds the platform's underlying exception text for the most recent failed element. For a validation rule violation, it includes the rule's custom error message. For a missing required field, it names the field. For an FLS denial, it names the field the user can't write.

The Fault path is your error-handling layer. Without it, the flow has nothing to say to the user. With it, the flow becomes self-describing.

The fixed example

The "New Opportunity From Account" flow with a Fault path and a defaulted CloseDate:

Element: Create Opportunity
Object: Opportunity
Field Assignments:
  Name = {!opportunityName}
  AccountId = {!recordId}
  Amount = {!amountInput}
  StageName = 'Prospecting'
  CloseDate = {!defaultCloseDate}  // 30 days from today

Connector: Success → "Confirmation Screen"
Connector: Fault → "Error Screen"

Element: Confirmation Screen
Content: "Opportunity created. Id: {!Create_Opportunity.Id}"

Element: Error Screen
Content:
  "We could not create that opportunity.

  Reason: {!$Flow.FaultMessage}

  Try again or contact the Sales Ops team in #sales-ops."

The user now sees the validation rule's custom message inline. If the message reads "CloseDate must be within 90 days for Prospecting opportunities," the rep fixes the input and retries. If the message reads "You don't have access to update Opportunity.Amount," the admin knows it's an FLS issue. The flow stays usable in production.

Designing for failure in flows

Three habits help, beyond the mechanical Fault-path wiring.

Assume every DML element will fail at least once. In ten percent of attempts in a real org, a Create or Update hits a validation rule the flow author didn't know about, a trigger that fires on insert, a workflow that locks a record, or a duplicate rule that flags the new row. Wire Fault paths from day one, not as a "later" task.

Use Get Records output safely. Always check the collection size before reading the first element. The Flow Builder lets you read {!collection[0]} directly, but if the collection is empty, the flow throws. Use a Decision element with the condition {!collection.size} > 0 to branch.

Wrap Apex actions in their own try/catch on the Apex side too. Even if the flow has a Fault connector, the Apex invocable should catch its own exceptions and return a structured error response. The flow can then read that response and present a domain-specific message, instead of the raw Apex exception text.

Reading the logs to find the real cause

When a flow throws and you need to find the underlying cause, the path is:

  1. Setup, Process Automation Settings, Email Address for Flow Errors. Set this to your developer team's inbox so faults email you with the full trace.
  2. Setup, Debug Logs, add the running user (or the Automated Process user, for record-triggered flows) and rerun the flow. The Apex log contains the full element-by-element trace.
  3. Inside the log, search for FLOW_INTERVIEW_FINISHED_LIMIT_USAGE or EXCEPTION_THROWN to find the failing element.

The email contains the most useful information. The debug log contains everything but takes longer to read. The Process Automation settings page is the first stop for any flow team.

Common underlying exceptions

A short tour of what $Flow.FaultMessage typically contains in real orgs:

  • "FIELD_CUSTOM_VALIDATION_EXCEPTION: <your validation rule's custom message>" when a validation rule blocks the DML.
  • "REQUIRED_FIELD_MISSING: Required fields are missing: [CloseDate]" when a required field isn't populated.
  • "INSUFFICIENT_ACCESS_OR_READONLY" when the running user can't perform the DML for permission reasons.
  • "UNABLE_TO_LOCK_ROW: unable to obtain exclusive access" when another transaction holds the record.
  • "DUPLICATE_VALUE: A duplicate of this record already exists" when a duplicate rule with Block action flags the new row.

Each of these is a different underlying issue. The Fault path lets the user (or the admin reviewing the screenshot) see which one fired without having to dig into the log.

Record-triggered flows behave differently

Screen Flows show the banner to the user. Record-triggered flows have no user context, so a thrown fault surfaces in a different place. The platform queues a fault email to the address configured in Process Automation Settings and writes the failure to the Paused and Failed Flow Interviews list under Setup.

If your record-triggered flow has no Fault path and no fault email recipient, the failure is silent. The record save still succeeds (record-triggered flows run after-save by default and don't block the DML), but the flow logic doesn't run. Users see no error. You only find out when the data is wrong days later.

Wire Fault paths into record-triggered flows even when they have no user-facing screen. Send the fault to a Platform Event or a custom logging object so you can monitor failures programmatically.

Sub-flows and inherited faults

A flow can call a sub-flow. If the sub-flow throws and has no Fault path of its own, the parent flow inherits the fault. If the parent doesn't catch it either, the banner shows on the user's screen.

The rule of thumb: every Subflow element in your parent flow should have a Fault connector. The Fault connector handles cases where the sub-flow itself fails or throws an uncaught exception inside one of its elements.

Closely related errors

SymptomLikely cause
"An unhandled fault has occurred in this flow"Missing Fault path on a failing element
"Apex action threw an exception"Invocable Apex method threw, no catch
Flow runs but does nothingDecision routed to End without DML, or after-save flow with no DML elements
Flow loops foreverLoop element with no exit condition or recursive sub-flow call

All four share a common ancestor: trusting the happy path. Defensive flow design assumes every element will fail at some point and builds the recovery path before shipping.

Defensive habits that pay off

Wire Fault paths to every DML and Action element. Set the Process Automation fault email. Add a Decision element after every Get Records to handle the empty-collection case. Test the failure paths in the Flow Debug view by passing inputs you know will fail. Build a reusable Error Screen subflow that displays $Flow.FaultMessage in a consistent format and call it from every parent flow.

The investment is small. The payoff is that the next time the banner fires in production, your user sees a real message and your admin knows where to look.

Further reading from Salesforce

Related dictionary terms

Share this fix

Share on LinkedInShare on X

Related Flow errors