function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Allan Hotchkiss 4Allan Hotchkiss 4 

Database.update not working as documented - throws exception for validation error

Hello,

I've created a batch job where I am updating some Account records.  In the job I am trying to run a Database.update call passing false as the 2nd parameter to enable saving of successful records even if some of them fail.  The problem I am having is that when 1 of the records fails due to a validation error, it is throwing an exception and not saving the rest of the records.  The documentation states this should not occur and that instead the records that didn't fail should be saved and the failed records be returned from the method call in a Database.SaveResult[] array.What is even stranger is that I have wrapped the Database.update call in a try..catch block and the exception isn't even caught.  I am thinking there is a bug somewhere in the platform causing this not to work as it previously used to.  Here is the code for the execute method in my batch job:
 
public void execute(Database.BatchableContext BC, List<sObject> recs) {

        for (sObject sObj : recs) {
            Account acct = (Account)sObj;
            acct.Growth_Rate__c = 0;
        }
        
        try {
            // Update Accounts
            Database.SaveResult[] srList = Database.update(recs, false);            
            
            Set<Id> errorIds = new Set<Id>();
            for (Database.SaveResult sr : srList) {
                if (!sr.isSuccess()) {
                    errorIds.add(sr.getId());
                }
            }
            System.debug('errorIds: ' + errorIds);
        }
        catch (Exception ex) {
			System.debug('Exception thrown: ' + ex.getMessage());
        }
}

Any help would be greatly appreciated.

Thanks,
Al
RaidanRaidan
Hi Al,

Are you sure the exception was thrown by this process? The update method is supposed to capture the exception behind the scene and store it in the SaveResult object - that is why the try-catch in your code doesn't seem to work.

I don't know if the code below works, but please give it a try. Basically, we try to update the account list not the generic sobject list (and btw, your execute method can also accept List<Account> instead of List<sObject>).
 
public void execute(Database.BatchableContext BC, List<sObject> recs) {
	List<Account> accts = (List<Account>) recs;
	for (Account acct : accts) {
		acct.Growth_Rate__c = 0;
	}

	Database.SaveResult[] srList = Database.update(accts, false);
	Set<Id> errorIds = new Set<Id>();
	for (Database.SaveResult sr : srList) {
		if (!sr.isSuccess()) {
			errorIds.add(sr.getId());
		}
		System.debug('errorIds: ' + errorIds);
	}
}

 
Allan Hotchkiss 4Allan Hotchkiss 4
Thanks for your response Raidan.  You are correct in that an exception is NOT being thrown but what is odd is that it is marking every record in the srList as errored with the same error.  So for example, if there are 2 records in the batch and only one of them has a validation error, the srList contains 2 records and neither record gets updated.  It should update the one record that didn't have a validation error.  Even more bewildering, it refuses to output any the System.debug() statements after the Database.update call. This behavior seems to only occur with validation errors.  If DML errors other than validation errors occur (i.e., too big of a number for a field), the Database.update call works as expected.

Finally, I reread the documentation and it appears that sr.getId() will only return an Id if the record update succeeds.  So I have changed the above code to the following:
public void execute(Database.BatchableContext BC, List<sObject> recs) {
        for (sObject sObj : recs) {
            Account acct = (Account)sObj;
            acct.Growth_Rate__c = 0;
        }
        
        try {
            // Update Accounts
            Database.SaveResult[] srList = Database.update(recs, false);            
            
            String errorString = '';
            for (Database.SaveResult sr : srList) {
                if (!sr.isSuccess()) {
                    for (Database.Error err : sr.getErrors()) {
						errorString += '=== Error Occurred === \r\n' + 'Message: ' + err.getMessage() + '\r\n' +
                            			'Fields: ' + err.getFields() + '\r\n' +
                            			'Status Code: ' + err.getStatusCode() + '\r\n\r\n';
                    }
                }
            }
            // When validation rule error occurs, this debug statement doesn't output anything.
            System.debug(errorString);
        }
        catch (Exception ex) {
			System.debug('Exception thrown: ' + ex.getMessage());
        }
}