Debugs Salesforce issues like Apex exceptions, governor limit breaches, and Flow failures using debug logs, SOQL explain plans, SF CLI tailing, and Developer Console.
How this skill is triggered — by the user, by Claude, or both
Slash command
/salesforce-claude-code:sf-debuggingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Reference: @../_reference/DEBUGGING_TOOLS.md
Reference: @../_reference/DEBUGGING_TOOLS.md
Via SF CLI (stream live logs):
# Stream all logs for the org in real time
sf apex tail log --target-org myOrg
# Stream logs with specific debug level
sf apex tail log \
--target-org myOrg \
--debug-level SFDC_DevConsole
# Retrieve a specific log by ID
sf apex get log \
--log-id 07L5e000000XXXXX \
--target-org myOrg
# List recent logs
sf apex list log --target-org myOrg
# Run anonymous Apex and capture log
sf apex run \
--file scripts/apex/debug-script.apex \
--target-org myOrg
# Run and save full log
sf apex run \
--file scripts/apex/debug-script.apex \
--target-org myOrg > debug-output.txt
Via Setup UI:
Via Developer Console:
sf org open --target-org myOrgDebug logs have a maximum size of 20 MB. Logs exceeding this are truncated from the middle. If you see gaps, reduce log level verbosity or narrow the operation scope.
15:23:01.001 (1234567)|EXECUTION_STARTED
15:23:01.012 (12345678)|CODE_UNIT_STARTED|[EXTERNAL]|execute_anonymous_apex
15:23:01.015 (15000000)|SOQL_EXECUTE_BEGIN|[12]|Aggregations:0|SELECT Id FROM Account
15:23:01.045 (45000000)|SOQL_EXECUTE_END|[12]|Rows:150
15:23:01.050 (50000000)|USER_DEBUG|[15]|DEBUG|Processing 150 accounts
15:23:01.200 (200000000)|DML_BEGIN|[22]|Op:Insert|Type:Contact|Rows:150
15:23:01.350 (350000000)|DML_END|[22]
15:23:01.400 (400000000)|CUMULATIVE_LIMIT_USAGE
Number of SOQL queries: 3 out of 100
Number of DML rows: 150 out of 10000
Maximum CPU time: 452 out of 10000
15:23:01.401 (401000000)|EXECUTION_FINISHED
Compare timestamps between BEGIN/END pairs to identify slow operations:
# Find slow operations by comparing BEGIN/END timestamps
grep -E "SOQL_EXECUTE_BEGIN|SOQL_EXECUTE_END|DML_BEGIN|DML_END" debug.log
// Open Developer Console > Execute Anonymous (Ctrl+E / Cmd+E)
Account acc = [SELECT Id FROM Account WHERE Name = 'Test Corp' LIMIT 1];
AccountService svc = new AccountService();
AccountService.AccountResult result = svc.getAccount(acc.Id);
System.debug(LoggingLevel.ERROR, JSON.serializePretty(result));
-- Developer Console > Query Editor tab
SELECT Id, Name, StageName, Amount, CloseDate
FROM Opportunity
WHERE StageName = 'Negotiation'
AND CloseDate = THIS_QUARTER
ORDER BY Amount DESC
LIMIT 25
Use "Query Plan" button to analyse query performance (see SOQL Query Plan section below).
-- BAD: Cost = 2.5 (TableScan)
SELECT Id FROM Account WHERE Description LIKE '%enterprise%'
-- GOOD: Cost = 0.1 (Index on ExternalId__c)
SELECT Id FROM Account WHERE ExternalId__c = 'ACC-001'
-- GOOD: Cost = 0.3 (Index on OwnerId)
SELECT Id FROM Account WHERE OwnerId = :currentUserId
Available in all editions with the Salesforce Extension Pack:
.log file in VS Code.cls filesRequires Performance Edition, Unlimited Edition, or Enterprise Edition add-on. Not available in Developer Edition.
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Apex Debugger",
"type": "apex",
"request": "launch",
"userIdFilter": [],
"requestTypeFilter": [],
"entryPointFilter": "",
"salesforceProject": "${workspaceRoot}"
}
]
}
.cls files (click gutter)Root cause: SOQL query inside a loop
// WRONG -- SOQL in loop
for (Account acc : Trigger.new) {
List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];
}
// FIX -- single query outside loop
Map<Id, List<Contact>> contactsByAccount = new Map<Id, List<Contact>>();
for (Contact c : [SELECT Id, AccountId FROM Contact WHERE AccountId IN :Trigger.newMap.keySet()]) {
if (!contactsByAccount.containsKey(c.AccountId)) {
contactsByAccount.put(c.AccountId, new List<Contact>());
}
contactsByAccount.get(c.AccountId).add(c);
}
Root cause: Complex nested loops, excessive string operations
// WRONG -- O(n^2) loop
for (Account acc : accounts) {
for (Contact con : allContacts) {
if (con.AccountId == acc.Id) { /* ... */ }
}
}
// FIX -- use Map for O(1) lookup
Map<Id, List<Contact>> contactsByAccount = buildContactMap(allContacts);
for (Account acc : accounts) {
List<Contact> accountContacts = contactsByAccount.get(acc.Id);
}
Root cause: Unchecked null reference
// PREFERRED (API 56.0+) -- null-safe navigation
String upperName = account.Name?.toUpperCase() ?? '';
String accountName = contact?.Account?.Name ?? 'No Account';
Root cause: Two concurrent transactions updating the same record(s). Fix with retry logic (Queueable), FOR UPDATE in SOQL, or reducing batch size.
Root cause: Setup objects (User, Profile) and non-setup objects in the same transaction. Separate with @future or System.runAs() in tests.
Root cause: DML on >10,000 records. Use Batch Apex to process in chunks.
Root cause: Synchronous callout in trigger context. Use @future(callout=true).
| Error | Root Cause | Fix |
|---|---|---|
| "An unhandled fault has occurred" | Missing fault connectors | Add fault paths on all DML/callout elements |
| Flow SOQL 101 limit exceeded | Get Records inside a loop | Move Get Records outside loop, use Collection Filtering |
| "This flow can't access the variable" | Variable not marked for input/output | Enable "Available for input/output" on the variable |
import { LightningElement, wire } from 'lwc';
export default class AccountCard extends LightningElement {
connectedCallback() {
console.group('AccountCard mounted');
console.log('accountId:', this.accountId);
console.groupEnd();
}
handleError(error) {
console.error('AccountCard error:', JSON.stringify(error));
}
}
Install "Salesforce Inspector Reloaded" for real-time metadata browsing, direct record access, API Inspector, and SOQL query runner.
public class DebugCalloutService {
public static HttpResponse send(HttpRequest req) {
System.debug(LoggingLevel.INFO, 'CALLOUT REQUEST: ' + req.getMethod() + ' ' + req.getEndpoint());
System.debug(LoggingLevel.FINE, 'REQUEST BODY: ' + req.getBody());
Http http = new Http();
HttpResponse res = http.send(req);
System.debug(LoggingLevel.INFO, 'CALLOUT RESPONSE: ' + res.getStatusCode());
System.debug(LoggingLevel.FINE, 'RESPONSE BODY: ' + res.getBody());
return res;
}
}
With CALLOUT: FINE level enabled:
CALLOUT_REQUEST|....|POST https://api.example.com/orders
CALLOUT_RESPONSE|....|200 {"orderId":"123","status":"OK"}
sf-review-agent -- for interactive, in-depth guidancesf-apex-constraints -- governor limits and Apex coding rulesnpx claudepluginhub jiten-singh-shahi/salesforce-claude-code --plugin salesforce-claude-codeApplies advanced Salesforce debugging via debug logs, SOQL query plans, and EventLogFile analysis for governor limits, slow queries, and complex bugs.
Analyzes Salesforce debug logs for root-cause diagnosis: governor limits, stack traces, slow queries, CPU/heap pressure. Scores issues 1-100 and recommends fixes.
Retrieves and analyzes Apex debug logs from Salesforce orgs to identify governor-limit hits, SOQL N+1 patterns, unhandled exceptions, and async job failures.