skills/development/debugging-techniques/SKILL.md
Comprehensive guide to debugging ServiceNow server-side and client-side code
npx skillsauth add happy-technologies-llc/happy-servicenow-skills debugging-techniquesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Master the art of debugging ServiceNow applications using built-in tools and techniques. This skill covers server-side debugging, client-side debugging, log analysis, and common error resolution.
admin or script_write for Script DebuggerServiceNow provides multiple logging functions for server-side scripts. Choose based on log level and visibility requirements.
| Function | Log Level | Use Case |
|----------|-----------|----------|
| gs.log(message, source) | Debug | Development/testing - legacy, prefer gs.debug |
| gs.debug(message) | Debug | Detailed troubleshooting (requires debug enabled) |
| gs.info(message) | Info | Informational messages for normal operations |
| gs.warn(message) | Warning | Potential issues that don't stop execution |
| gs.error(message) | Error | Errors that need attention |
Best Practice: Always include context in log messages:
// BAD - No context
gs.info('Record updated');
// GOOD - Full context
gs.info('[IncidentHandler] Incident ' + current.number + ' updated by ' + gs.getUserName() +
' - State changed from ' + current.state.getDisplayValue() + ' to ' + previous.state.getDisplayValue());
Querying System Logs:
Tool: SN-Query-Table
Parameters:
table_name: syslog
query: source=IncidentHandler^sys_created_onONToday@javascript:gs.beginningOfToday()@javascript:gs.endOfToday()
fields: sys_created_on,level,message,source
limit: 50
order_by: sys_created_on
order_by_desc: true
The Script Debugger allows step-by-step execution of server-side scripts.
Enabling the Script Debugger:
Debugging Business Rules:
Tool: SN-Query-Table
Parameters:
table_name: sys_script
query: collection=incident^active=true
fields: name,order,when,script,condition
limit: 50
order_by: order
Key Features:
Enable detailed logging for specific components during your session.
Common Session Debug Modules:
| Module | Purpose | Enable Path |
|--------|---------|-------------|
| Session Debug | All session debugging | System Diagnostics > Session Debug > All |
| Business Rules | Log BR execution | System Diagnostics > Session Debug > Business Rules |
| SQL | Log database queries | System Diagnostics > Session Debug > SQL |
| Security | ACL debugging | System Diagnostics > Session Debug > Security |
| Client Scripts | Log client script data | System Diagnostics > Session Debug > Client Scripts |
Enable via Script:
// Enable business rule debugging for current session
gs.setProperty('glide.debugger.current.enabled', 'true');
Track script execution for troubleshooting.
Tool: SN-Query-Table
Parameters:
table_name: sys_script_execution_history
query: sys_created_onONLast 1 hour@javascript:gs.hoursAgo(1)@javascript:gs.nowDateTime()
fields: script,execution_time,status,error_message,sys_created_on
limit: 100
order_by: sys_created_on
order_by_desc: true
| Method | Purpose |
|--------|---------|
| console.log() | General logging |
| console.info() | Informational messages |
| console.warn() | Warning messages |
| console.error() | Error messages |
| console.table() | Display data in table format |
| console.trace() | Show call stack |
ServiceNow-Specific:
// jslog - ServiceNow legacy logging (outputs to console when enabled)
jslog('Client script executed: ' + g_form.getValue('number'));
// Check if debug mode is enabled
if (window.NOW && NOW.debug) {
console.log('[DEBUG] Form loaded for: ' + g_form.getValue('sys_id'));
}
Chrome DevTools Workflow:
scripts.do?sysparm_type= or in the page sourceUseful DevTools Commands:
// In Console - Inspect form field
g_form.getValue('short_description');
// Get all form values
g_form.serialize();
// Check if field is mandatory
g_form.isMandatory('priority');
// Inspect GlideAjax response
// Set breakpoint in callback function to inspect 'response' object
Monitor AJAX calls and API requests:
xmlhttp.do - GlideAjax callsapi/now/ - REST API callssysparm_processor - Form submissionsCommon Network Issues:
Tool: SN-Query-Table
Parameters:
table_name: syslog_transaction
query: sys_created_onONLast 1 hour@javascript:gs.hoursAgo(1)@javascript:gs.nowDateTime()^urlLIKEincident
fields: url,response_time,user,sys_created_on,status
limit: 50
order_by: response_time
order_by_desc: true
Tool: SN-Query-Table
Parameters:
table_name: syslog
query: messageLIKEslow query^sys_created_onONToday@javascript:gs.beginningOfToday()@javascript:gs.endOfToday()
fields: sys_created_on,message,source
limit: 100
Business rules execute in this order for each operation (insert, update, delete):
Critical: Order Matters!
Tool: SN-Query-Table
Parameters:
table_name: sys_script
query: collection=incident^active=true^when=before
fields: name,order,when,condition,script
limit: 100
order_by: order
Debugging Order Issues:
// Add to each business rule to trace execution order
gs.info('[BR Debug] ' + current.sys_class_name + ' - ' +
'Name: ' + current.getTableName() + '_BR_Name - ' +
'Order: ' + 100 + ' - ' +
'When: before - ' +
'Record: ' + current.number);
Check if Script is Running:
(function executeRule(current, previous) {
gs.info('[BR_NAME] Script starting for: ' + current.getDisplayValue());
// Your logic here
gs.info('[BR_NAME] Script completed');
})(current, previous);
Debug Condition:
// Log condition evaluation
gs.info('[BR_NAME] Condition check - current.state: ' + current.state +
', previous.state: ' + previous.state +
', Result: ' + (current.state != previous.state));
Inspect Current vs Previous:
// Log all changed fields
var changes = current.changesLog();
if (changes) {
gs.info('[BR_NAME] Changes: ' + changes);
}
Query Workflow Context:
Tool: SN-Query-Table
Parameters:
table_name: wf_context
query: active=true
fields: name,state,started,ended,id,scratchpad
limit: 50
order_by: sys_created_on
order_by_desc: true
Check Workflow History:
Tool: SN-Query-Table
Parameters:
table_name: wf_history
query: context.active=false^sys_created_onONLast 7 days@javascript:gs.daysAgo(7)@javascript:gs.nowDateTime()
fields: activity,from_state,to_state,additional_info,sys_created_on
limit: 100
order_by: sys_created_on
order_by_desc: true
Query Flow Executions:
Tool: SN-Query-Table
Parameters:
table_name: sys_flow_context
query: state!=complete^ORstate=error
fields: name,state,started,ended,error_message
limit: 50
order_by: sys_created_on
order_by_desc: true
Enable Flow Debug Mode:
Cause: GlideRecord query returned no results Solution:
var gr = new GlideRecord('incident');
gr.addQuery('number', 'INC0000001');
gr.query();
if (gr.next()) {
// Record found
} else {
gs.warn('Record not found: INC0000001');
}
Cause: ACL blocking access Solution:
Tool: SN-Query-Table
Parameters:
table_name: sys_security_acl
query: name=incident
fields: name,operation,condition,script,role
limit: 100
Cause: JavaScript syntax error or runtime exception Solution:
Cause: Script running too long (30+ seconds) Solution:
Cause: Same record being updated by multiple processes Solution:
if (current.update_in_progress) return;
current.update_in_progress = true;
Cause: Too many nested GlideRecord operations or recursive calls Solution:
| Tool | Purpose |
|------|---------|
| SN-Query-Table | Query syslog, execution history, script records |
| SN-Execute-Background-Script | Run diagnostic scripts on the instance |
| SN-Get-Record | Retrieve specific log or script records |
| Endpoint | Method | Purpose |
|----------|--------|---------|
| /api/now/table/syslog | GET | Query system logs |
| /api/now/table/sys_script_execution_history | GET | Query script execution history |
| /api/now/table/syslog_transaction | GET | Query transaction logs |
| Tool | Purpose |
|------|---------|
| Bash | Execute curl commands for REST API |
| Read | Read local script files for comparison |
[Component] Action - DetailsSymptom: gs.log/gs.info statements not showing in System Logs Cause: Log level filtered or property disabled Solution:
glide.log.levelglide.script.log.level allows your log levelgs.error() which always logsSymptom: Breakpoints not hit when script executes Cause: Wrong session, script cached, or condition not met Solution:
Symptom: Business rule appears inactive despite being enabled Cause: Condition not met, wrong table, or filter condition Solution:
Tool: SN-Query-Table
Parameters:
table_name: sys_script
query: name=YOUR_BUSINESS_RULE_NAME
fields: name,active,collection,when,order,condition,filter_condition,script
limit: 1
// Add to business rule for debugging
(function executeRule(current, previous) {
var startTime = new Date().getTime();
var brName = 'Validate Incident Priority';
gs.info('[' + brName + '] START - Record: ' + current.number);
try {
// Original business rule logic
if (current.priority == 1 && !current.assignment_group) {
gs.info('[' + brName + '] Setting mandatory assignment group');
gs.addErrorMessage('Critical incidents require an assignment group');
current.setAbortAction(true);
}
} catch (e) {
gs.error('[' + brName + '] ERROR: ' + e.message + '\nStack: ' + e.stack);
}
var duration = new Date().getTime() - startTime;
gs.info('[' + brName + '] END - Duration: ' + duration + 'ms');
})(current, previous);
Tool: SN-Query-Table
Parameters:
table_name: syslog
query: level=error^sys_created_onONLast 24 hours@javascript:gs.hoursAgo(24)@javascript:gs.nowDateTime()
fields: sys_created_on,source,message
limit: 100
order_by: sys_created_on
order_by_desc: true
// Client Script
function onLoad() {
console.log('[MyClientScript] Form loaded, fetching data...');
var ga = new GlideAjax('MyAjaxProcessor');
ga.addParam('sysparm_name', 'getData');
ga.addParam('sysparm_record_id', g_form.getUniqueValue());
ga.getXMLAnswer(function(response) {
console.log('[MyClientScript] Response received:', response);
if (!response) {
console.error('[MyClientScript] Empty response from server');
return;
}
try {
var data = JSON.parse(response);
console.table(data);
} catch (e) {
console.error('[MyClientScript] Failed to parse response:', e);
}
});
}
Tool: SN-Execute-Background-Script
Parameters:
script: |
// Find all errors in last hour with stack traces
var gr = new GlideRecord('syslog');
gr.addQuery('level', 'error');
gr.addQuery('sys_created_on', '>=', gs.hoursAgo(1));
gr.orderByDesc('sys_created_on');
gr.setLimit(20);
gr.query();
var results = [];
while (gr.next()) {
results.push({
time: gr.sys_created_on.getDisplayValue(),
source: gr.source.toString(),
message: gr.message.toString().substring(0, 200)
});
}
gs.info('=== Recent Errors ===\n' + JSON.stringify(results, null, 2));
description: Analyze recent error logs
development/script-includes - Writing reusable server-side scriptsdevelopment/business-rules - Business rule development and best practicesdevelopment/client-scripts - Client-side scripting techniquesadministration/system-logs - System log configuration and managementtesting
Manage supplier onboarding, qualification, performance monitoring, and offboarding with auditable lifecycle controls
tools
Identify emerging risks, prioritize intake signals, and route candidates into formal GRC risk assessment workflows
documentation
Screen inbound documents for completeness, policy risk, and routing readiness before extraction or case workflows
testing
Generate concise task summaries with status, timeline, blockers, SLA risk, and recommended next actions