According to support, this is as designed, although it seems more like an oversight than a design decision to me. In any case, it should be documented.
From support:
"We believeThe behavior that itswe see when we are using List is working as designed and it designed. Please find the detailed summary for the same below
-- The getDMLFieldName's return type is String[] whereas getDMLFields return type is Schema.sObjectField[]
which means the getDMLFields has to do withmake a describe call while access metadatato get the fields. However I am discussing this case with our Internal Team for someThe describe call looks something like follows
Account.sObjectType.getDescribe();
We need concrete information based on which we can identifySObject Types like Account or Opportunity to get this Describe call working. As Heterogeneous list List cannot be used to get the behavior of DMLExceptioninstance and map it to the describe call.getDmlFields when we
Hence kindly use Homogeneous List types like List or List to get the getDMLFields() working."
As a work-around, you can get the SObjectField
if you keep a reference to the list of SObjects on which the failed DML operation was performed.
List<SObject> sobjects = new List<SObject>{new Account()};
try {
insert sobjects;
} catch (DMLException e) {
for (Integer i = 0; i < e.getNumDml(); i++) {
// Get the list of field names, which may not be globally unique,
// but will be unique for a single SObject
List<String> fieldNames = e.getDmlFieldNames(i);
// Get the SObjectType for the record that failed
Integer insertIndex = e.getDmlIndex(i);
Schema.SObjectType failedType = sobjects[insertIndex].getSObjectType();
for (String fieldName : fieldNames) {
// Use the field name to get the SObjectField
Schema.SObjectField fieldFromName = failedType.getDescribe().fields.getMap().get(fieldName);
System.debug('SOBJECT FIELD FROM NAME');
System.debug(fieldFromName);
}
}
}