When using Database.insert() with allOrNone = false (partial success mode enabled), the record Id logged in an after insert Apex trigger during the initial DML attempt may differ from the final committed Id returned by Database.SaveResult.
For example, a developer logs the record Id inside an after insert trigger and later finds that the SaveResult returns a different Id for what appears to be the same record.
In a partial-success DML transaction, Salesforce internally retries the DML operation after removing the records that failed validation or caused errors. During this retry, Salesforce generates new record Ids for the successfully inserted records. The Ids that were assigned during the first (failed) attempt are discarded. As a result, the Id logged inside the trigger during the first attempt differs from the Id in the final committed SaveResult.
This behavior is specific to partial-success DML operations using Database.insert(records, false) or Database.insert(records, dmlOptions) with AllowSave = true.
Steps to Reproduce:
trigger AccountTrigger on Account (after insert) {
for (Account acc : Trigger.new) {
System.debug('After Insert Account Name: ' + acc.Name);
System.debug('After Insert Account Id: ' + acc.Id);
}
}
List<Account> accountsToInsert = new List<Account>();
// Valid record
accountsToInsert.add(new Account(
Name = 'Successful Account'
));
// Invalid record: Account Name is required
accountsToInsert.add(new Account());
Database.SaveResult[] results = Database.insert(accountsToInsert, false);
System.debug('---- FINAL SAVE RESULTS ----');
for (Integer i = 0; i < results.size(); i++) {
if (results[i].isSuccess()) {
System.debug('Success Index: ' + i);
System.debug('Final committed Id from SaveResult: ' + results[i].getId());
} else {
System.debug('Failed Index: ' + i);
for (Database.Error err : results[i].getErrors()) {
System.debug('Error: ' + err.getStatusCode() + ' - ' + err.getMessage());
}
}
}
The debug log shows entries from the `after insert` trigger for the valid record.
These entries can appear for the first trigger invocation and again for the retry invocation.
The `Database.SaveResult` output shows the final committed Id for the successfully inserted record.
In case partial-success scenario, The Id logged during the first trigger invocation will be different from the final committed Id returned by `SaveResult`.
When Database.insert() is called with allOrNone = false:
after insert trigger fires once during the first attempt (with temporary Ids) and once during the retry (with the final committed Ids).If your code uses static variables in triggers to prevent re-entry (a common pattern for preventing recursive trigger execution), be aware that the static variable set during the first trigger execution will still be true during the retry. This can cause your trigger logic to be skipped on the retry pass, resulting in incomplete processing for the successfully inserted records.
To handle this safely, design your static variable guards to count executions rather than using a simple boolean, or reset them explicitly in the catch block of your DML error handling logic.
005386519

We use three kinds of cookies on our websites: required, functional, and advertising. You can choose whether functional and advertising cookies apply. Click on the different cookie categories to find out more about each category and to change the default settings.
Privacy Statement
Required cookies are necessary for basic website functionality. Some examples include: session cookies needed to transmit the website, authentication cookies, and security cookies.
Functional cookies enhance functions, performance, and services on the website. Some examples include: cookies used to analyze site traffic, cookies used for market research, and cookies used to display advertising that is not directed to a particular individual.
Advertising cookies track activity across websites in order to understand a viewer’s interests, and direct them specific marketing. Some examples include: cookies used for remarketing, or interest-based advertising.