ApexSensei

Drills DML operations

Catch a DmlException and read its details

DML operations · Intermediate · 14 min · Runs in your Salesforce Developer org · Apex Path

Default DML is all-or-none: if any row fails, Apex throws `DmlException`, and the exception knows which row failed and why. Catch it from an all-or-none insert of a valid and an invalid Account, then read the failed row's index and status code. Because nothing is saved under all-or-none, a follow-up query also returns zero.

Lesson

Why this matters

Bulk loaders and migrations run DML on dozens of rows at once. When one row is wrong, the default insert throws DmlException and nothing saves. You need to know which row failed and why, so the next retry can skip or fix just that row.

The smallest working example

try {
    insert rows;
} catch (DmlException dmlError) {
    Integer failedIndex = dmlError.getDmlIndex(0);
    String  code        = String.valueOf(dmlError.getDmlType(0));
    System.debug(failedIndex);
    System.debug(code);
}

The index points into the input list. The status is a Salesforce enum like REQUIRED_FIELD_MISSING.

Name the parts

  • **try { insert rows; }** runs the DML. If any row fails, the rest of the block is skipped.
  • **catch (DmlException dmlError)** names the type thrown by DML errors. Other exceptions do not match.
  • **getDmlIndex(0)** returns the first failed row's position in the input list.
  • **getDmlType(0)** returns the Salesforce status enum (for example REQUIRED_FIELD_MISSING).
  • All-or-none DML means a single failure rolls back the whole statement. A later query sees no saved rows.

A common wrong version

try {
    insert rows;
} catch (Exception e) {
    System.debug('something failed');
}

Catching plain Exception works, but you lose the row-level helpers. e.getDmlIndex(0) does not compile on an Exception variable.

The fix

Catch DmlException so you can read the row-level details:

try {
    insert accounts;
} catch (DmlException dmlError) {
    failedIndex = dmlError.getDmlIndex(0);
    statusCode  = String.valueOf(dmlError.getDmlType(0));
}

Now you try

Wrap the starter's insert accounts in try/catch (DmlException dmlError), read index and status in the catch, then count what was saved. Print Failed index:, Status code: and Saved count: in that order.

Learn more

The challenge

Insert a list with one valid and one invalid Account and read what went wrong from the DmlException.

Examples:

  • The invalid row is the second entry → Failed index: 1
  • The missing field is Name → Status code: REQUIRED_FIELD_MISSING
  • Nothing is saved because of all-or-none → Saved count: 0

Requirements:

  1. catch (DmlException should be present around the insert.
  2. .getDmlIndex should be present in the catch.
  3. Failed index: 1 should be printed from the caught exception.
  4. Status code: REQUIRED_FIELD_MISSING should be printed from the caught exception.
  5. Saved count: 0 should be printed from a count query by tag.

Starter code

String tag = 'AS-D310-' + UserInfo.getUserId() + '-' +
    String.valueOf(Datetime.now().getTime());
List<Account> accounts = new List<Account>{
    new Account(Name = tag + '-Valid'),
    new Account()
};

Integer failedIndex = -1;
String statusCode = 'NONE';

Related drills

Try this drill free

You write the Apex yourself. ApexSensei runs it and tells you what passed and what did not.

‹ Insert an Account with Contacts · Undelete from the Recycle Bin ›