One can read about sporadic DUPLICATE_VALUE errors during Upsert in many forums. They mostly occur in Dataloader scenarios with a root cause being Oracle race conditions. But what I have here is not sporadic, but occurs every single time in a specific customer org running our managed app.
The error occurs in a custom save action called on the EDIT page of a custom object. Clicking "Save" also update the child records using this method. I use upsert as the page needs to work for New and Edit scenarios (Should I replace it with insert / upsert?)
public PageReference doSave() {
// Note: Required as below try/catch would prevent the platforms default behaviour to rollback on error
Savepoint toBeforeState = Database.setSavepoint();
// Note: Without try/catch DML errors would be shown as ugly white error page
try {
upsert parent;
for(Child__c child : children) {
if(child.Id == null) {
child.mdr_Parent__c = parent.Id;
}
}
upsert children;
}
catch(Exception ex) {
ApexPages.addMessages(ex);
Database.rollback(toBeforeState);
// Note: Workaround for Ref.1
removeInvalidIds();
}
PageReference result = (ApexPages.hasMessages()) ? null : new PageReference('/' + parent.Id);
return result;
}
Ref.1: "Data not available" when using Database.rollback() in custom save action
This how we reproduce this error in a single customer org:
- Save and provoke an custom (unpackaged) validation error on the packaged Child__c
- Validation Error is displayed
- Correct error and click save again
- DUPLICATE_VALUE is displayed as follows
System.DmlException: Upsert failed. First exception on row 1; first error: DUPLICATE_VALUE, duplicate value found: unknown duplicates value on record with id: a0IZ0000003WUaY: []
The record of the provided Id does NOT exist after that. I guess the error comes from a Unique field on the child record which is set by a WF rule:
OR( ISNEW(), ISCHANGED( Field1__c ), ISCHANGED( Field2__c ))
Maybe ISNEW() is triggered even by an Upsert on an existing record?!
We are unable to reproduce this in our DE org (even with validation rules) BUT I can stop this error in the customer org when I turn off a workflow rule that is performing a Field Update on the same object.