Create your own
Lesson illustration

Identifying Governor Limits from Debug Logs

Hello again. In the previous lesson, you traced record access from object and field permissions through organization-wide defaults, hierarchy access, sharing rules, teams, and manual sharing. That was a who can access which record diagnosis.

This lesson shifts to a different kind of production diagnosis: a transaction that fails because it consumed too much of a shared Salesforce resource. Debug-log interpretation is a practical skill for developer work and a frequent interview prompt. By the end, you will be able to identify the specific governor limit responsible for a failure, distinguish it from an ordinary application error, and locate the operation or automation most likely responsible.


Governor limits are transaction budgets

Salesforce is a multi-tenant platform: many organizations share underlying infrastructure. A governor limit is therefore a hard budget for a resource used during one transaction, such as database queries, DML operations, CPU time, heap memory, or callouts.

A transaction is one unit of work. For example, saving one Account can invoke validation rules, record-triggered Flows, Apex triggers, and additional DML on related records. Their resource use contributes to the same transaction budget.

Watch this short introduction for the platform rationale.

Apex Governor Limits | Chapter 91 | Salesforce Developer Masterclass

Watch “Apex Governor Limits” by Salesforce Makes Sense for the core reason governor limits exist: Salesforce allocates finite shared resources across tenants.

Watch the foundation. Focus on the idea of a fixed per-transaction ceiling rather than trying to memorize every possible limit.

Two consequences matter during debugging:

  1. Limits are cumulative. Ten queries in a Flow, ninety-one queries in Apex, and a query in a trigger can collectively breach the synchronous SOQL-query limit.
  2. A limit breach fails the transaction. The transaction is rolled back; prior DML work in that transaction does not persist. The log, however, still records the resource consumption that led to the failure.

For common synchronous Apex transactions, these are the signatures worth recognizing first:

ResourceTypical synchronous ceilingFailure wording to recognize
SOQL queries100Too many SOQL queries: 101
SOQL query rows50,000Too many query rows: 50001
DML statements150Too many DML statements: 151
DML rows10,000Too many DML rows: 10001
CPU time10,000 msApex CPU time limit exceeded
Heap size6 MBApex heap size too large
Callouts100Too many callouts: 101

The exact ceilings can differ by execution context—for example, asynchronous jobs have different limits. When analyzing a real incident, treat the exception text and the limit shown in that log as the authority.

A useful distinction:

  • A SOQL query limit counts statements issued.
  • A query-row limit counts records returned across queries.
  • A DML statement limit counts DML operations such as insert, update, delete, or Database.update.
  • A DML-row limit counts records affected.

One bulk update accountsToUpdate; may use one DML statement but affect hundreds of DML rows. This distinction prevents a common incorrect diagnosis.


Learn the log’s structure before chasing the failure

A raw debug log is chronological. Its lines record execution events, including code-unit boundaries, SOQL, DML, Flow or validation activity, debug statements, exceptions, and usage snapshots.

A Salesforce Developer Console debug-log view: `LIMIT_USAGE_FOR_NS` entries in the execution log correspond to the Limits tab, which summarizes SOQL queries, returned rows, DML, CPU time, heap use, and callouts for the transaction.

In the screenshot, the LIMIT_USAGE_FOR_NS entries show consumption such as “Number of SOQL queries: 19 out of 100.” The lower Limits panel presents the same information in a summary form. Modern tooling and raw logs may look different, but the key labels remain useful.

Read this concise orientation to the useful boundaries in a log.

How do I start debugging an error in Salesforce? | Thinqloud

Read Thinqloud’s explanation of execution units and cumulative resource usage. It gives you a mental map for locating the transaction-wide evidence rather than interpreting a single line in isolation.

In the “Execution Units” discussion, read the cumulative-usage explanation. Notice that a trigger, test method, anonymous execution, or asynchronous execution can form a meaningful diagnostic boundary, while the whole transaction may contain several nested code units.

The event names to recognize are:

Log eventWhat it tells you
EXECUTION_STARTED / EXECUTION_FINISHEDOuter boundary of the transaction
CODE_UNIT_STARTED / CODE_UNIT_FINISHEDA trigger, class, Flow-related unit, test method, or other unit of work began or ended
SOQL_EXECUTE_BEGIN / SOQL_EXECUTE_ENDA database query ran; the end entry commonly reports returned rows
DML_BEGIN / DML_ENDA DML operation was attempted or completed
LIMIT_USAGE_FOR_NSA resource-usage snapshot for a namespace
CUMULATIVE_LIMIT_USAGEA transaction-level usage summary, when present
EXCEPTION_THROWN / FATAL_ERRORThe direct failure evidence

USER_DEBUG lines can provide helpful landmarks, but they are not proof of what failed. A line such as USER_DEBUG|DEBUG|starting update merely tells you where custom logging happened. The decisive evidence is normally the exception line.


A reliable diagnosis method

When a user reports a failure, do not start by counting every SOQL or DML line manually. Use this sequence instead.

1. Confirm that it is a governor-limit failure

Search the raw log for these terms, in this order:

  • FATAL_ERROR
  • LimitException
  • Too many
  • CPU time
  • heap
  • LIMIT_USAGE_FOR_NS

A governor-limit failure generally contains System.LimitException or a clear platform limit message. Contrast it with:

  • FIELD_CUSTOM_VALIDATION_EXCEPTION, which is a validation-rule failure;
  • DUPLICATE_VALUE, which is a duplicate-record error;
  • System.QueryException, which may be malformed SOQL or an unexpected query result;
  • System.DmlException, which may wrap a field, validation, or sharing-related DML problem.

Those errors can be serious, but they are not automatically governor-limit failures.

2. Read the exception literally

Suppose the end of a log contains:

EXCEPTION_THROWN|[42]|System.LimitException: Too many SOQL queries: 101
FATAL_ERROR|System.LimitException: Too many SOQL queries: 101

The responsible limit is the SOQL-query statement limit. The number 101 tells you the transaction attempted its 101st query in a synchronous context where the ceiling was 100.

You do not need to infer that this is a “performance problem” in general. State the precise finding:

The transaction failed because it exceeded the SOQL-query governor limit: it attempted 101 queries where 100 were permitted.

That level of specificity is what a reviewer, interviewer, or incident ticket needs.

3. Cross-check the usage summary—but do not let it overrule the exception

Look for CUMULATIVE_LIMIT_USAGE or LIMIT_USAGE_FOR_NS near the end of the log. You might see:

Number of SOQL queries: 100 out of 100
Number of DML statements: 12 out of 150
Maximum CPU time: 1840 out of 10000

This supports the SOQL diagnosis. It also rules out DML or CPU as the primary failure.

However, a snapshot can show 100 out of 100 even though the exception says “Too many SOQL queries: 101.” The 101st attempt failed, so it may not appear as a completed, counted operation in every summary. When they appear to differ, trust the explicit System.LimitException message for the failure cause.

4. Move upward from the exception to find the immediate trigger

The line number in an exception is often the most direct clue:

EXCEPTION_THROWN|[42]|System.LimitException: Too many DML statements: 151

Look upward for the enclosing CODE_UNIT_STARTED entry. It tells you whether the failing instruction came from:

  • an Apex class or trigger;
  • a test method;
  • a Visualforce controller;
  • a Flow-related code path;
  • another automation layer.

Then inspect the immediately preceding relevant operations. Before a SOQL failure, find the final SOQL_EXECUTE_BEGIN. Before a DML failure, find the final DML_BEGIN.

This identifies the failing instruction, not necessarily the whole design defect. The 101st query is simply where the transaction ran out of budget; the earlier hundred queries may have come from multiple triggers, handlers, or Flows.

5. Trace the cumulative path, not only the last line

After locating the immediate failure, work backward through the transaction’s major code units. Ask:

  • Which automation began this transaction?
  • What additional triggers or Flows ran because of DML?
  • Is the same handler or query pattern repeated?
  • Does the log show a query or DML operation inside repeated execution?

The prior lesson’s order-of-execution context matters here: a record update can activate several automation mechanisms. A class may have only twenty SOQL queries on its own, but it can still be part of a transaction that has already consumed eighty-one through other automation.


Recognizing the usual failure patterns

The failure text identifies which budget was exceeded. The surrounding log points to why it happened.

Observed failureWhat the log usually showsLikely underlying pattern
Too many SOQL queriesRepeated SOQL_EXECUTE_BEGIN entries, often in the same code unitQuery inside a loop; several automations querying the same records independently
Too many query rowsOne or more SOQL_EXECUTE_END entries with very large row countsUnselective query; querying an unnecessarily broad relationship or data set
Too many DML statementsRepeated DML_BEGIN entriesinsert, update, or delete inside a loop; recursive automation
Too many DML rowsA bulk operation affects more than 10,000 total recordsAutomation expands one change into too many related record updates
Apex CPU time limit exceededQuery/DML counts may remain below their ceilings; long execution duration or repeated logicNested loops, expensive calculations, recursion, or too much combined automation
Apex heap size too largeFrequent or large HEAP_ALLOCATE entries; huge collections or serialized payloadsLoading too many fields/records, retaining data unnecessarily, large JSON or strings
Too many calloutsRepeated callout eventsOne HTTP callout per record rather than batching or redesigning async work

Watch the log-reading demonstration before examining the examples below.

Debug Logs - Explained | Chapter 92 | Salesforce Developer Masterclass

Watch “Debug Logs - Explained” by Salesforce Makes Sense for a visual walkthrough of timestamps, events, error lines, and limit usage in a debug log.

Watch reading the log. Focus on the relationship between the chronological execution events and the governor-limit usage summary; this is the relationship you will use in every diagnosis.

Pattern A: SOQL inside a loop

Imagine a trigger handling 200 incoming Cases. Its handler loops over Cases and runs a SOQL query once for each Case. The log may show many repeated query events followed by:

EXCEPTION_THROWN|[18]|System.LimitException: Too many SOQL queries: 101
FATAL_ERROR|System.LimitException: Too many SOQL queries: 101

Correct diagnosis: SOQL-query governor limit exceeded.

Probable cause: A query is executed repeatedly, often from inside a loop. The repair is not “raise the limit,” because Apex limits cannot be raised for this transaction. The later Apex module will formalize the refactoring pattern: collect needed IDs in a set, issue one query outside the loop, and use a map for lookup.

Pattern B: DML inside a loop

A second handler constructs one Task for each incoming record and executes insert task; during every loop iteration. The relevant ending may be:

DML_BEGIN|[31]|Op:Insert|Type:Task|Rows:1
EXCEPTION_THROWN|[31]|System.LimitException: Too many DML statements: 151
FATAL_ERROR|System.LimitException: Too many DML statements: 151

Correct diagnosis: DML-statement governor limit exceeded.

Do not call this a “DML row” failure simply because the code inserts records. A DML statement was issued 151 times. The usual correction is to collect all Tasks in a list and insert the list once.

Pattern C: CPU limit without a query or DML limit breach

CPU failures can be more deceptive:

EXCEPTION_THROWN|System.LimitException: Apex CPU time limit exceeded
FATAL_ERROR|System.LimitException: Apex CPU time limit exceeded

The cumulative section might report only 30 SOQL queries and 20 DML statements—comfortably within their independent limits. The failure is still unambiguous: CPU time is the exhausted resource.

Now inspect the execution path for repeated trigger or Flow invocations, nested loops, expensive string or collection processing, and formulas or automation repeatedly recalculated. Avoid the shortcut “there are lots of queries, so it must be SOQL.” A transaction can have many queries and still fail first on CPU.


A compact incident-reporting format

After reviewing the log, communicate the result in three parts:

  1. Failure: name the exact governor limit and exception text.
  2. Location: identify the failing code unit, class/method/line if available, and the initiating record action.
  3. Cause hypothesis: state the observed pattern, separating evidence from inference.

For example:

The Account update transaction failed with System.LimitException: Too many DML statements: 151. The failure occurred in CaseFollowUpHandler.createTasks, invoked by the Case after-update trigger. The log shows repeated single-row DML_BEGIN insert operations in that handler, which indicates DML is being performed per record rather than in bulk.

This is stronger than “the trigger has a governor-limits issue.” It is concise, falsifiable, and gives the next developer a useful starting point.

When you have access to Salesforce tooling, begin with a trace flag for the user who reproduces the problem, then retrieve the matching log from Setup, Debug Logs or inspect it in Developer Console. Capture the smallest reproducible action possible. A noisy log containing unrelated user activity makes it much harder to establish the true transaction boundary.


Key takeaways

A governor-limit diagnosis is evidence-led:

  • First find FATAL_ERROR, System.LimitException, or explicit “Too many” wording.
  • Name the exact resource: SOQL statements, query rows, DML statements, DML rows, CPU time, heap size, or callouts.
  • Use LIMIT_USAGE_FOR_NS and CUMULATIVE_LIMIT_USAGE as confirmation, while treating the exception line as decisive.
  • Trace upward through CODE_UNIT_STARTED, SOQL, and DML events to find the immediate instruction and the wider automation chain.
  • Do not confuse a governor limit with validation, duplicate, sharing, or ordinary query errors.
  • Report the failure, location, and evidence-based root-cause hypothesis separately.

Next, you will begin the Apex, SOQL, and transaction-control module by implementing an Apex class with typed methods, constructors, access modifiers, and interfaces.

Can't find a good explanation? Sign up and we'll make it for you

Sign up