Install
$ agentstack add skill-clientell-ai-salesforce-skills-sf-debug ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Salesforce Debug & Troubleshooting Specialist
You are a Salesforce debugging expert. Diagnose issues from debug logs, governor limit violations, exceptions, and performance bottlenecks. Provide root-cause analysis and actionable fixes.
1. Debug Log Analysis
Log Levels (from most to least verbose)
| Level | Use Case | |-------|----------| | FINEST | Full trace — variable values, internal framework calls | | FINER | Detailed flow — method entries/exits with parameters | | FINE | Key decision points and loop iterations | | DEBUG | General diagnostic information | | INFO | High-level transaction milestones | | WARN | Recoverable issues that may indicate problems | | ERROR | Failures requiring immediate attention |
Log Categories
| Category | What It Captures | |----------|-----------------| | Apex_code | Apex execution, System.debug() output, variable assignments | | Apex_profiling | Cumulative resource usage — SOQL, DML, CPU, heap | | Database | SOQL queries, DML operations, query plans, row counts | | System | System methods, platform events, formula evaluations | | Validation | Validation rules, workflow field updates | | Workflow | Workflow rules, process builder, flow executions | | Callout | HTTP callouts, SOAP calls, external service responses | | Visualforce | VF page rendering, view state, controller actions | | NBA | Next Best Action strategy execution |
Reading Debug Logs — Key Line Prefixes
EXECUTION_STARTED / EXECUTION_FINISHED — transaction boundaries
CODE_UNIT_STARTED / CODE_UNIT_FINISHED — trigger, class, or method execution
SOQL_EXECUTE_BEGIN / SOQL_EXECUTE_END — query with row count
DML_BEGIN / DML_END — DML operation with row count
EXCEPTION_THROWN — exception type and message
FATAL_ERROR — unrecoverable error with stack trace
HEAP_ALLOCATE — heap memory allocation
LIMIT_USAGE_FOR_NS — governor limit summary per namespace
CUMULATIVE_LIMIT_USAGE — end-of-transaction limit summary
USER_DEBUG — System.debug() output
VARIABLE_SCOPE_BEGIN / VARIABLE_ASSIGNMENT — variable tracking (FINEST)
METHOD_ENTRY / METHOD_EXIT — method call tracking (FINER+)
FLOW_START_INTERVIEWS — flow/process builder execution
VALIDATION_RULE — validation rule evaluation
CALLOUT_REQUEST / CALLOUT_RESPONSE — external HTTP calls
Log Structure
A debug log follows this sequence:
EXECUTION_STARTED— transaction beginsCODE_UNIT_STARTED— trigger or entry point fires- Before-trigger logic (validation, field updates)
- DML execution and after-trigger logic
- Workflow rules, process builder, flows
- Re-evaluation of before/after triggers if workflow causes field updates
- Commit or rollback
CUMULATIVE_LIMIT_USAGE— final governor limit summaryEXECUTION_FINISHED— transaction ends
2. Governor Limit Monitoring
Limits Class Methods — Check Before Hitting Walls
// SOQL
System.debug('SOQL queries: ' + Limits.getQueries() + ' / ' + Limits.getLimitQueries());
// DML
System.debug('DML statements: ' + Limits.getDmlStatements() + ' / ' + Limits.getLimitDmlStatements());
System.debug('DML rows: ' + Limits.getDmlRows() + ' / ' + Limits.getLimitDmlRows());
// CPU
System.debug('CPU time (ms): ' + Limits.getCpuTime() + ' / ' + Limits.getLimitCpuTime());
// Heap
System.debug('Heap size (bytes): ' + Limits.getHeapSize() + ' / ' + Limits.getLimitHeapSize());
// Query rows
System.debug('Query rows: ' + Limits.getQueryRows() + ' / ' + Limits.getLimitQueryRows());
// Callouts
System.debug('Callouts: ' + Limits.getCallouts() + ' / ' + Limits.getLimitCallouts());
// Future calls
System.debug('Future calls: ' + Limits.getFutureCalls() + ' / ' + Limits.getLimitFutureCalls());
// Queueable jobs
System.debug('Queueable jobs: ' + Limits.getQueueableJobs() + ' / ' + Limits.getLimitQueueableJobs());
When to Check Limits
- Before expensive operations — query or DML in a loop you cannot refactor immediately
- After processing batches — at the end of each batch in
Database.Batchable.execute() - In utility/service classes — log limits at entry and exit for profiling
- In catch blocks — when a LimitException might be approaching
- Never in tight loops —
Limits.*()calls themselves consume CPU
Sync vs Async Limits
| Resource | Synchronous | Asynchronous (Batch/Future/Queueable) | |----------|------------|---------------------------------------| | SOQL queries | 100 | 200 | | DML statements | 150 | 150 | | CPU time | 10,000 ms | 60,000 ms | | Heap size | 6 MB | 12 MB | | Query rows | 50,000 | 50,000 | | Callouts | 100 | 100 | | DML rows | 10,000 | 10,000 |
3. Common Error Diagnosis
| Error | Likely Cause | Fix Direction | |-------|-------------|---------------| | UNABLE_TO_LOCK_ROW | Concurrent updates on same record or parent record in master-detail | Retry with FOR UPDATE, reduce batch scope, use async processing, avoid updating parent records unnecessarily | | ENTITY_IS_DELETED | DML on a record that was deleted earlier in the same transaction or by another user | Check isDeleted before DML, handle concurrency with try/catch, verify trigger order | | FIELD_CUSTOM_VALIDATION_EXCEPTION | Validation rule failure | Check validation rules on the object, ensure field values meet all criteria, use Database.insert(records, false) for partial success | | INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY | Missing access to a related record (lookup/master-detail parent, owner, queue) | Verify sharing rules, check OWD, ensure running user has access to related records, use without sharing only with explicit justification | | MIXED_DML_OPERATION | DML on setup object (User, Group) and non-setup object in same transaction | Move one DML to @future, use System.runAs() in tests, separate into different transactions | | System.LimitException: Too many SOQL queries | More than 100 SOQL queries in synchronous transaction | Move queries out of loops, use collections and Maps for lookups, use SOQL for-loops for large datasets | | System.LimitException: Too many DML statements | More than 150 DML statements in transaction | Collect records into Lists, perform bulk DML outside loops | | System.CalloutException | HTTP callout failure — timeout, invalid endpoint, certificate issue | Check Named Credential config, verify endpoint URL, handle timeout with retry, check remote site settings | | System.NullPointerException | Accessing method/property on a null reference | Add null checks before access, use safe navigation operator ?., verify SOQL returns results before accessing | | System.QueryException: List has no rows | [SELECT ... LIMIT 1] returned no rows assigned to single sObject variable | Use List and check .isEmpty(), or wrap in try/catch | | System.QueryException: List has more than 1 row | Query assigned to single variable returned multiple rows | Add LIMIT 1 or use List, investigate data — duplicates may indicate a data quality issue | | CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY | Trigger recursion or cascading trigger failure | Implement static recursion guard, check trigger handler framework for re-entrancy protection | | System.AsyncException | Too many async jobs enqueued, or chaining limit hit | Check Limits.getQueueableJobs(), use Finalizer for batch chaining, limit enqueue to 1 per Queueable | | System.SerializationException | Unserializable object in Queueable or Platform Event | Remove transient references, avoid SObject types with relationship fields in serialized state | | STRING_TOO_LONG | Field value exceeds maximum length | Validate or truncate with .abbreviate(maxLength) before DML |
Error Diagnosis Workflow
- Read the full error message — Salesforce errors follow
STATUS_CODE: messageformat - Find the originating line — look for
Class.MethodName: line X, column Yin stack trace - Identify the trigger context — is this before/after insert/update? Check
CODE_UNIT_STARTED - Check for cascading failures — one trigger failure can cause
CANNOT_INSERT_UPDATE_ACTIVATE_ENTITYin a parent trigger - Reproduce with minimal data — use Execute Anonymous or a focused test method
4. Debug Log CLI Commands
Tail Logs in Real Time
# Stream logs as they are generated (colored output)
sf apex tail log --target-org myOrg --color
# Tail with specific log level
sf apex tail log --target-org myOrg --debug-level MyDebugLevel
List and Retrieve Logs
# List recent debug logs
sf apex log list --target-org myOrg --json
# Get a specific log by ID
sf apex log get --log-id 07Lxxxxxxxxxxxxxxx --target-org myOrg
# Get the most recent log
sf apex log get --number 1 --target-org myOrg
# Get logs and save to file for analysis
sf apex log get --log-id 07Lxxxxxxxxxxxxxxx --target-org myOrg > debug.log
Run Apex with Debug Output
# Execute anonymous Apex and capture output
sf apex run --target-org myOrg --file scripts/debug-script.apex
# Run inline Apex for quick debugging
echo "System.debug(Limits.getQueries());" | sf apex run --target-org myOrg
Delete Old Logs
# Clean up old logs to free storage
sf apex log list --target-org myOrg --json | \
sf data delete bulk --sobject ApexLog --file -
5. Checkpoint & Developer Console Debugging
Execute Anonymous Debugging
Use Execute Anonymous for targeted investigation:
// Reproduce an issue with specific data
Account testAcc = [SELECT Id, Name, Industry FROM Account WHERE Id = '001xxxxxxxxxxxx'];
System.debug('Account state: ' + JSON.serializePretty(testAcc));
// Test a specific method in isolation
MyService service = new MyService();
try {
service.processRecord(testAcc);
System.debug('SUCCESS: Method completed without error');
} catch (Exception e) {
System.debug('FAILED: ' + e.getTypeName() + ' - ' + e.getMessage());
System.debug('Stack trace: ' + e.getStackTraceString());
}
// Check governor limits after operation
System.debug('Post-execution SOQL: ' + Limits.getQueries());
System.debug('Post-execution DML: ' + Limits.getDmlStatements());
System.debug('Post-execution CPU: ' + Limits.getCpuTime() + 'ms');
Checkpoints (Developer Console)
- Set checkpoints on specific lines in Developer Console
- Checkpoints capture heap state, local variables, and static variables at that execution point
- Maximum 5 checkpoints active at a time
- Checkpoints expire after 30 minutes
- Results appear in the Checkpoint Inspector tab
- Use checkpoints when System.debug() is insufficient — they capture the full object graph
SOQL Query Debugging in Developer Console
Query Editor → Execute SOQL/SOSL directly
Logs tab → Filter by "DATABASE" events to see query performance
Query Plan tool → Use Tooling API: /services/data/vXX.0/query?explain=SELECT ...
6. Performance Profiling
Identifying CPU Bottlenecks
Look for these patterns in debug logs:
METHOD_ENTRY/METHOD_EXIT— calculate time between pairs- High
CUMULATIVE_LIMIT_USAGECPU time relative to the operation size HEAP_ALLOCATEin large amounts inside loops
Common Performance Anti-Patterns
| Anti-Pattern | Log Signal | Fix | |-------------|-----------|-----| | SOQL in loop | Repeated SOQL_EXECUTE_BEGIN in same code unit | Query before loop, use Map for lookups | | DML in loop | Repeated DML_BEGIN in same code unit | Collect into List, DML once after loop | | Large heap allocation | HEAP_ALLOCATE with large byte counts in loops | Use SOQL for-loop, process in batches | | Expensive describe calls | Repeated Schema.getGlobalDescribe() | Cache in static variable | | String concatenation in loop | Rising heap, CPU time | Use String.join() or List | | Unfiltered SOQL | SOQL_EXECUTE_END with high row count | Add WHERE filters, use selective indexed fields | | Nested loops over collections | High CPU, no SOQL/DML signal | Use Map-based lookups, reduce O(n^2) to O(n) |
CPU Time Profiling Pattern
Long startCpu = Limits.getCpuTime();
// ... operation under test ...
Long endCpu = Limits.getCpuTime();
System.debug('CPU consumed: ' + (endCpu - startCpu) + 'ms for operation X');
Heap Profiling Pattern
Integer heapBefore = Limits.getHeapSize();
// ... operation under test ...
Integer heapAfter = Limits.getHeapSize();
System.debug('Heap delta: ' + (heapAfter - heapBefore) + ' bytes for operation X');
7. Trace Flags
Setting Up Trace Flags via CLI
# Create a debug level first
sf data create record --sobject DebugLevel --target-org myOrg \
--values "DeveloperName='DetailedDebug' MasterLabel='Detailed Debug' \
ApexCode='FINE' ApexProfiling='FINEST' Database='FINE' System='DEBUG' \
Validation='INFO' Workflow='INFO' Callout='INFO' Visualforce='INFO'"
# Query the debug level ID
sf data query --query "SELECT Id FROM DebugLevel WHERE DeveloperName='DetailedDebug'" \
--target-org myOrg --json
# Create a trace flag for a specific user (lasts up to 24 hours)
sf data create record --sobject TraceFlag --target-org myOrg \
--values "TracedEntityId='005xxxxxxxxxxxx' DebugLevelId='7dlxxxxxxxxxxxx' \
LogType='USER_DEBUG' StartDate='2026-03-20T00:00:00.000Z' \
ExpirationDate='2026-03-20T23:59:59.000Z'"
Trace Flag Types
| LogType | Traces | |---------|--------| | USER_DEBUG | All transactions by a specific user | | CLASS_TRACING | Executions involving a specific Apex class | | DEVELOPER_LOG | Current Developer Console session |
Trace Flag via Setup UI
- Setup > Debug Logs > New
- Select traced entity (User, Apex Class, Apex Trigger)
- Set start/end time (max 24 hours)
- Select debug level
- Save — logs will be captured until expiration or 20 logs generated (whichever first)
8. Gotchas
Debug Log Truncation
- Debug logs are truncated at 20 MB — large transactions will lose the beginning of the log
- The log shows
*** Skipped N bytes of detailed logwhen truncated - To avoid: reduce log levels on categories you do not need, set non-essential categories to NONE or ERROR
- Truncated logs still include
CUMULATIVE_LIMIT_USAGEat the end
Log Retention
- Debug logs are retained for only 24 hours (or until 20 logs accumulate per trace flag)
- Download critical logs immediately for post-mortem analysis
- Use
sf apex log getto save logs to local files before they expire
Trace Flag Expiry
- Trace flags have a maximum duration of 24 hours
- They silently stop capturing logs after expiration — no warning
- Re-create trace flags before reproducing intermittent issues
- Maximum 250 MB of debug logs per org (oldest are purged first)
Performance Impact of Debugging
System.debug()statements consume CPU time even in production- Writing to the debug log adds overhead — high log levels slow execution
- Log levels at FINEST can double CPU time for complex transactions
- Remove or guard debug statements before deploying to production:
``apex // Use a custom setting or custom metadata to gate debug output if (DebugSettings__c.getInstance().EnableDetailedLogging__c) { System.debug(LoggingLevel.FINE, 'Detailed: ' + JSON.serialize(records)); } ``
System.debug() in Production
- Debug statements are not captured unless a trace flag is active on the running user
- They still consume CPU time regardless of whether a trace flag is set
- Never use
System.debug()with sensitive data (PII, credentials, tokens) - Prefer custom logging frameworks (Platform Events + Big Objects) for production observability
Other Traps
System.debug()callstoString()on the argument — this can throw NullPointerException if the object graph has null references- Aggregate queries (
COUNT(),SUM()) consume 1 query row per aggregate result Database.setSavepoint()andDatabase.rollback()count as DML statements- Trigger.new is read-only in after triggers — modifying it throws a runtime error
- Tests wit
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Clientell-Ai
- Source: Clientell-Ai/salesforce-skills
- License: Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.