System.LimitException: Too many DML statements: 151
Apex limits a single transaction to 150 DML statements (insert/update/upsert/delete/merge calls). The cap is on the **number of statements**, not the number of records — `update largeList` is 1 statement; `update one;` 200 times is 200 statements.
Also seen asToo many DML statements: 151·Too many DML statements·System.LimitException: Too many DML
The fix is structural and almost mechanical: collect, then DML once.
The 1-vs-200 distinction
The governor counts each insert / update / upsert / delete / merge keyword (or each Database.* call) as one statement. The platform doesn't care how many records are in the list:
update bigList; // 1 DML statement, even if bigList has 9,000 records
for (Account a : bigList) update a; // 9,000 DML statements. Throws on #151.
This means almost every appearance of this error is a DML call inside a loop, and the cure is the same as for Too many SOQL queries: 101: hoist it out.
The pattern
// Wrong: one DML per pass.
for (Account a : Trigger.new) {
if (a.Annual_Revenue__c == null) {
a.Annual_Revenue__c = 0;
update a;
}
}
// Right: build a list, DML once at the bottom.
List<Account> toUpdate = new List<Account>();
for (Account a : Trigger.new) {
if (a.Annual_Revenue__c == null) {
a.Annual_Revenue__c = 0;
toUpdate.add(a);
}
}
if (!toUpdate.isEmpty()) update toUpdate;
If you're inside a before trigger, you don't need the DML at all — just mutate Trigger.new directly and the platform persists it for free with no governor cost.
Bonus: triggers chaining triggers
A trigger doing update on records of a different SObject can fire that SObject's trigger, which can fire workflow rules, which can trigger more Apex... and all of those count against your 150 budget. When you can't find a loop in your own code, check what your DML triggers downstream — process automations, flows, and workflow field updates can all eat into the cap.
System.debug('DML so far: '
+ Limits.getDmlStatements() + '/' + Limits.getLimitDmlStatements()
+ ', rows touched: '
+ Limits.getDmlRows() + '/' + Limits.getLimitDmlRows());
getDmlRows() checks the other DML governor — 10,000 rows total per transaction. You can blow past that with a single huge update list even though you only used 1 of your 150 statements.
