Flow: changes from this flow won't be saved because of the Save Order of Execution
A before-save record-triggered flow tried to do something only after-save can do (update related records, make callouts) — or an after-save flow tried to update the same record (which before-save handles cheaper). Each phase has different capabilities. Pick the right phase.
Also seen asSave Order of Execution·before-save flow·after-save flow·Flow changes won't be saved
A flow architect builds a record-triggered flow on Opportunity that sets a calculated forecast amount when the stage changes to Closed Won. The flow is configured to run before-save. The Apex trigger that already exists on Opportunity also writes to the same forecast field with a different formula. When users save Closed Won Opportunities, the value in the field flickers and then settles on whichever automation ran last. The save sometimes shows the warning "Changes from this flow won't be saved because of the Save Order of Execution."
What the platform is checking
Salesforce executes saves through a fixed Save Order of Execution. The order is documented and stable: validation rules, before-save flows, before-triggers, custom validation, duplicate rules, save to database, after-save flows, after-triggers, assignment rules, auto-response rules, workflow rules, escalation rules, entitlement rules, roll-up summary recalculation, criteria-based sharing recalculation, parent record updates, and finally commit.
When two automations write to the same field in different phases of the order, only one write wins. The order determines which. A field set by a before-save flow can be overwritten by a before-trigger. A field set in a before-trigger can be re-evaluated by a workflow field update later in the order. The platform warns when it detects that a flow's changes will be undone by a subsequent step in the order.
The warning is precise about the symptom (the flow's changes will not survive) but vague about the cause. The cause is always the same: another automation in a later phase is writing to the same field. Finding that other automation requires walking through the Save Order of Execution and identifying every place the field is set.
The save order is multi-pass for triggers and roll-ups. A workflow field update that meets re-evaluation criteria can re-trigger before-triggers, which can themselves cause another round of evaluations. Two or three passes through the order are possible for a single API save.
What is in conflict
Three common patterns produce most flow-vs-order conflicts.
Flow and Apex trigger write the same field. A before-save flow sets Forecast_Amount__c based on stage and probability. A before-update Apex trigger sets the same field with a slightly different formula. Both run in the before-save phase, but Apex triggers run after before-save flows. The trigger wins.
Flow and workflow field update conflict. A before-save flow sets Status__c to "Qualified". A workflow rule with a field update sets Status__c to "Working" when certain conditions are met. Workflow runs later in the order; if its criteria match, it overwrites the flow's value.
Flow updates a related record that updates back. A flow on Opportunity updates the parent Account's Open_Pipeline__c field. An Apex trigger on Account responds to the change by recalculating the field with its own logic. The flow's value is preserved on the Account briefly, then replaced by the trigger's value.
The broken example
A before-save record-triggered flow on Opportunity:
Flow: Set_Forecast_Amount
Trigger: Record-Triggered
When: A record is created or updated
Object: Opportunity
Optimize for: Fast Field Updates (before-save)
Entry Condition: ISCHANGED(StageName) AND StageName = 'Closed Won'
Assignment:
Forecast_Amount__c = Amount * 0.95
The flow sets the forecast amount to 95 percent of the Amount when the stage flips to Closed Won. The intent is to discount for fulfillment risk.
An Apex trigger from a previous engineer:
trigger OpportunityTrigger on Opportunity (before update) {
for (Opportunity o : Trigger.new) {
if (o.StageName == 'Closed Won' && o.Amount != null) {
Decimal probability = o.Probability ?? 100;
o.Forecast_Amount__c = o.Amount * (probability / 100);
}
}
}
The trigger sets the same field using probability instead of a fixed 95 percent multiplier. Both automations target the same field on the same save. The trigger runs after the before-save flow. The flow's value lives for microseconds before the trigger overwrites it.
A second shape: a flow that updates an Account from an Opportunity context:
Flow: Refresh_Account_Pipeline
Object: Opportunity
Trigger: After Save
Update Related Record:
Account (related to this Opportunity)
Open_Pipeline__c = sum of related open Opportunity amounts
A trigger on Account recomputes Open_Pipeline__c whenever the Account is updated. The flow updates the Account, the trigger fires, and the trigger re-computes the field with its own logic. Whatever the flow wrote is lost.
The fix, three paths
Consolidate the logic in one place. The cleanest fix removes the duplicate automation. Either the flow owns the field calculation or the trigger does. The team picks one and removes the other.
For the broken example, deactivate the Apex trigger's forecast logic and let the flow own it:
trigger OpportunityTrigger on Opportunity (before update) {
for (Opportunity o : Trigger.new) {
// Forecast amount now set by Set_Forecast_Amount flow
if (o.StageName != 'Closed Won' && o.Forecast_Amount__c != null) {
o.Forecast_Amount__c = null;
}
}
}
The trigger retains a small responsibility (clearing the forecast when the stage moves away from Closed Won) but defers the calculation to the flow. The save now has a single source of truth for the field.
Move the flow to a different phase. If both automations must run, the flow can move to the after-save phase. After-save flows run after triggers, so the flow's writes survive trigger writes.
Flow: Set_Forecast_Amount
Trigger: Record-Triggered
When: A record is updated
Object: Opportunity
Optimize for: Actions and Related Records (after-save)
The after-save flow runs near the end of the order. Its writes go to the database via a Update Records element rather than direct field assignment. The flow's value lands in the final commit even when other automations wrote the same field earlier.
The trade-off is an extra DML statement. The before-save flow modifies the record in place at no cost. The after-save flow requires its own update, which counts against governor limits and adds a transaction step.
Use conditional logic to avoid overwrites. When both automations should sometimes win, build explicit precedence into the field updates. The trigger checks whether the flow already set the field and skips if so.
trigger OpportunityTrigger on Opportunity (before update) {
for (Opportunity o : Trigger.new) {
if (o.StageName == 'Closed Won' && o.Forecast_Amount__c == null) {
Decimal probability = o.Probability ?? 100;
o.Forecast_Amount__c = o.Amount * (probability / 100);
}
}
}
The trigger calculates only when the flow did not set a value. The flow takes precedence; the trigger acts as a fallback. The contract is explicit and testable.
The fixed example
A consolidated approach where the flow owns the forecast calculation and the trigger handles cleanup:
Flow: Set_Forecast_Amount (before-save)
Object: Opportunity
Entry Condition: ISCHANGED(StageName) OR ISCHANGED(Amount)
Decision:
IF StageName = 'Closed Won':
Forecast_Amount__c = Amount * 0.95
ELSE IF StageName IN ('Negotiation', 'Proposal'):
Forecast_Amount__c = Amount * (Probability / 100)
ELSE:
Forecast_Amount__c = null
And the trigger now leaves the forecast field alone:
trigger OpportunityTrigger on Opportunity (before insert, before update) {
OpportunityTriggerHandler.run(Trigger.new, Trigger.oldMap);
}
public class OpportunityTriggerHandler {
public static void run(List<Opportunity> newRecords, Map<Id, Opportunity> oldMap) {
for (Opportunity o : newRecords) {
updateOwnerEmail(o);
stampSourceSystem(o);
// Forecast_Amount__c is owned by Set_Forecast_Amount flow
}
}
}
The flow handles forecasting end-to-end. The trigger handles owner email, source system stamping, and other concerns the flow does not touch. Each field has one writer; conflicts cannot arise.
Edge cases and gotchas
Recalculate When Updated workflow option. Workflow rules have a "Re-evaluate Workflow Rules after Field Change" option. When enabled, a field update from one workflow can re-trigger other workflows and re-fire the entire chain. The Save Order of Execution loops until no more re-triggers happen or until a cycle limit is hit. Flow updates inside this loop can be overwritten multiple times.
Validation rules in before-save flows. A before-save flow that throws (via a Fault path or a custom validation) can prevent the save entirely. The validation surfaces as an error to the user, not as a "changes not saved" warning. Distinguishing between "my flow's changes didn't survive" and "my flow stopped the save" requires reading the error carefully.
Formula fields recalculate immediately. A formula field that references a field the flow updates reflects the new value as soon as the flow sets it. If the trigger then overwrites the underlying field, the formula recalculates to the trigger's value. There is no visible flicker because the formula is computed on read.
Process Builder is being retired. Existing Process Builders in the same transaction as a flow can produce subtle ordering issues. Salesforce is migrating Process Builder users to Flow. New automations should use Flow; existing Process Builders should be migrated as resourcing allows.
Cross-object updates from after-save flows. An after-save flow that updates a related record (the parent Account, the Opportunity Contact Role) is a separate DML. The related record's own triggers and flows run as part of that DML. The order of execution on the related record is independent of the originating record's order.
Order matters for triggers within a class. When multiple Apex triggers exist on the same object and event, they execute in an undefined order. The team's discipline (one trigger per object, dispatching to a handler class) prevents conflicts among Apex code. The flow vs trigger conflict is separate.
Defensive habits
Maintain a registry of which automation owns which field. A simple table of Object.Field -> owning automation prevents the dual-ownership scenario that produces this warning. New developers consult the registry before writing automation against an existing field.
Prefer before-save flows for field calculations. Before-save flows are fast, do not consume governor budget for DML, and run inside the record's own transaction. After-save flows are appropriate for related-record updates, callouts, and other side effects.
Move existing triggers' simple field calculations to flow. A trigger that exists purely to compute a derived field is a candidate for replacement with a before-save flow. The flow is easier to maintain, visible to administrators, and consumes less of the Apex governor budget.
Test the full Save Order of Execution. A test that updates an Opportunity in the way users do, then asserts the final field values, catches order-of-execution conflicts. Unit tests on individual automations miss the cross-automation interactions.
When two automations must coexist, document the precedence. A comment in the Apex trigger and a description on the flow that explain who wins under what conditions is the difference between a maintainable design and a future incident.
Test patterns
An integration test that exercises the full save:
@IsTest
static void closedWonOpportunityForecastIsFlowValue() {
Opportunity opp = new Opportunity(
Name = 'Test',
StageName = 'Prospecting',
CloseDate = Date.today().addDays(30),
Amount = 100000,
Probability = 50
);
insert opp;
Test.startTest();
opp.StageName = 'Closed Won';
update opp;
Test.stopTest();
Opportunity reloaded = [SELECT Forecast_Amount__c FROM Opportunity WHERE Id = :opp.Id];
System.assertEquals(95000, reloaded.Forecast_Amount__c, 'Flow should set 95% of Amount');
}
The test verifies the final value after all automations have run. If a trigger overwrites the flow's value, the assertion fails. Regression tests cover every field that has a designated owner.
Diagnosing in production
When the warning appears:
- Identify the field the flow is writing.
- Search Apex triggers, classes, flows, workflows, processes, and approval processes for any other automation that writes the same field.
- Determine the Save Order of Execution phase of each automation.
- Decide which automation should own the field.
- Refactor the others to not write that field.
The Setup, Schema Builder, and the Optimizer all help find dependencies. For complex orgs, a metadata search tool that scans the source repo for the field name is faster.
Quick recovery checklist
- Find the conflicting automation.
- Consolidate field ownership.
- Test the full save flow end-to-end.
- Document the new contract.
- Add a regression test that asserts the expected field value.
Save order conflicts are usually a sign of unclear ownership. Once ownership is established, the warnings go away and the data behaves predictably.
Further reading from Salesforce
- Salesforce Help: Triggers and Order of Execution
- Apex Developer Guide: Triggers and Order of Execution
- Salesforce Help: Best Practices for Building Flows
- Architect: Process Automation Decision Guide
- Trailhead: Build Flows with Flow Builder
Related dictionary terms
Share this fix
Related Flow errors
An unhandled fault has occurred in this flow
FlowA 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 …
Couldn't find the subflow with name <X> or no active version
FlowThe parent flow tries to invoke a subflow that either doesn't exist in this org or has no active version. Subflows must be deployed AND acti…
Number of iterations exceeded. The flow can't perform more than 2,000 loop iterations.
FlowA Flow `Loop` element ran more than 2,000 iterations. The fix is to operate on the *collection* directly (Update Records, Get Records with f…
The Apex action <name> couldn't be invoked because it's not @InvocableMethod or has the wrong signature
FlowA Flow tried to call an Apex method as an action, but the method either lacks the `@InvocableMethod` annotation, doesn't return / accept a `…
The flow failed to access the value for {!variable} because it hasn't been set or assigned
FlowA flow tried to read a variable that no element ever wrote to. Either an upstream Get Records returned nothing (so the result variable staye…