Top ServiceNow Errors & Their Solutions






Navigating the ServiceNow Maze: Common Errors and Practical Fixes


Navigating the ServiceNow Maze: Common Errors and Practical Fixes

ServiceNow is a formidable platform, a true powerhouse for digital workflows and enterprise service management. Yet, like any intricate system, it has its quirks, its hidden corners, and its moments where you just scratch your head and wonder, “Why isn’t this working?!” We’ve all been there – staring at a form that won’t submit, a field that refuses to behave, or an update set that’s throwing cryptic errors.

This article is your friendly guide through some of the most common ServiceNow errors, challenges, and best practices. We’ll unwrap them with a human touch, offering practical solutions, real-world scenarios, and insights that might just save your day (or ace that interview!). So, grab your favorite beverage, and let’s dive into the common pitfalls and how to elegantly sidestep them.

Access and Permissions: The Gatekeepers of Your Instance

Security is paramount in ServiceNow, and often, the first hurdle you encounter is related to who can see what, and who can do what. Misconfigured permissions can lead to frustrating “Access Denied” messages or, worse, unintended data exposure. Let’s demystify how access works.

Managing User and Group Permissions

The core of ServiceNow access control lies in roles. You can assign roles directly to users or, for a more sustainable approach, to groups. So, what’s the best practice here?

Fix & Best Practice: Always assign roles to groups, not individual users. This isn’t just a suggestion; it’s a golden rule. When an employee leaves or changes departments, removing them from a group automatically revokes all associated roles, ensuring a clean and secure offboarding. Manual role assignments to individual users can quickly become an unmanageable mess.

// Best practice: Assign roles to groups. Users get roles by being members of groups.
// This example illustrates the concept, typically done via UI or HR integrations.
// Add a user to a group that already has the 'itil' role.
var grUser = new GlideRecord('sys_user');
grUser.addQuery('user_name', 'john.doe');
grUser.query();

var grGroup = new GlideRecord('sys_user_group');
grGroup.addQuery('name', 'IT Support'); // Assuming 'IT Support' group exists and has 'itil' role
grGroup.query();

if (grUser.next() && grGroup.next()) {
    var grMember = new GlideRecord('sys_user_grmember');
    grMember.initialize();
    grMember.user = grUser.sys_id;
    grMember.group = grGroup.sys_id;
    grMember.insert();
    gs.info('User ' + grUser.user_name + ' added to group ' + grGroup.name);
}

Access Control Lists (ACLs) and the `security_admin` Role

ACLs are the bedrock of record-level security. They define who can create, read, write, or delete records in your tables. To even touch an ACL, you need a powerful key:

Fix: The `security_admin` role is required to create or modify ACLs. This is a special elevated role, often temporary, to prevent accidental security breaches. Remember to elevate your role to `security_admin` before working with ACLs, and then de-elevate when done.

Interview Question: “Which role is required to work on access control?”
Answer: `security_admin`. Simple, yet crucial.

When you create a new custom table, ServiceNow doesn’t just leave it wide open. It intelligently creates some initial ACLs:

Understanding: By default, four ACLs are created for a new table (read, write, create, delete) if the “Create access controls” checkbox is checked during table creation. If you uncheck it, no ACLs are created, giving you a clean slate but also full responsibility for security.

`GlideRecord` vs. `GlideRecordSecure`: Security by Default

When writing server-side scripts, you interact with the database using `GlideRecord`. But did you know there’s a more secure variant?

Best Practice: Use `GlideRecordSecure` when performing CRUD (Create, Read, Update, Delete) operations in server-side scripts. It automatically enforces ACLs and other security rules. While `GlideRecord` requires you to manually check permissions (`canCreate()`, `canWrite()`, etc.), `GlideRecordSecure` handles it out-of-the-box, significantly reducing the risk of security vulnerabilities.

Controlling UI Elements: Hiding New/Edit Buttons

Sometimes, you want to restrict who can add or modify records in related lists, even if they have general table access.

Fix: To hide “New” and “Edit” buttons in a related list for certain users, you can click on the three dots (Column Options) in the related list header and select “Omit new button” or “Omit edit button”. This allows for UI-level control based on roles or conditions. You can also configure this by modifying the “Omit New Button” and “Omit Edit Button” fields on the Related List definition.

User Criteria: Tailoring Catalog and Knowledge Access

Beyond roles, User Criteria offers a granular way to control access to specific items.

Understanding: User Criteria is specifically used to restrict access to Catalog Items, Record Producers, and Knowledge Articles. It provides more flexible conditions than simple role checks, allowing you to define who can *see* or *contribute* based on groups, departments, companies, or even scripts.

Incident, Problem, and Change Management: Keeping Your Services Running Smoothly

The ITIL processes are at the heart of ServiceNow. However, managing Incidents, Problems, and Changes can throw up specific challenges, especially when dealing with dependencies and workflows.

Distinguishing Incidents and Problems

This is a fundamental concept, often tested in interviews, and crucial for effective IT service management.

Understanding: An Incident is an unplanned interruption to an IT service or a reduction in the quality of an IT service. A Problem arises when the same issue repeatedly affects a single employee, indicating an underlying root cause. If the same problem impacts multiple people simultaneously, it’s typically still categorized as an Incident (often a “major incident” or a “parent incident”) with other incidents becoming its “child incidents.”

Automating Parent-Child Incident Closure

When a major incident is resolved, you don’t want to manually close dozens of related child incidents.

Fix: Implement an After Business Rule on the Incident table. This rule triggers when a parent incident’s state changes to “Closed” (assuming state value 7). It then finds all child incidents associated with that parent and updates their state to “Closed”.

// Business Rule: Close Child Incidents
// Table: Incident
// When: After
// Update: true
// Condition: current.state.changesTo(7)
if (current.state == 7 && current.parent == '') {
    var grChild = new GlideRecord('incident');
    grChild.addQuery('parent', current.sys_id);
    grChild.query();

    while (grChild.next()) {
        grChild.state = 7; // Set the state to Closed
        grChild.update(); // Update the child incident
    }
}

Preventing Incident Closure with Open Tasks

You wouldn’t want to mark an incident as closed if there are still active tasks associated with it, right? This ensures thoroughness in incident resolution.

Fix: Use a Before Business Rule (or an `onSubmit` Client Script for client-side feedback) to check for open incident tasks before allowing the incident to close. If open tasks exist, abort the action and provide an error message.

// Business Rule: Prevent Incident Closure with Open Tasks
// Table: Incident
// When: Before
// Update: true
// Condition: current.state.changesTo(7) // Or your 'Closed' state value
var grTask = new GlideRecord('incident_task');
grTask.addQuery('incident', current.sys_id);
grTask.addQuery('state', '!=', 3); // Assuming 3 is 'Closed' state for incident_task
grTask.query();

if (grTask.hasNext()) {
    gs.addErrorMessage('Cannot close the incident because there are open tasks.');
    current.setAbortAction(true); // Stop the incident from closing
}

This logic can be extended to Problem and Change Request records with their respective task tables (Problem Task, Change Task).

Closing Associated Incidents from a Problem

When the root cause (Problem) is resolved, all related incidents should ideally be closed too.

Fix: Implement an After Business Rule on the Problem table. When the Problem is closed, it identifies and closes all associated incidents.

// Business Rule: Close Related Incidents from Problem
// Table: Problem
// When: After
// Update: true
// Condition: current.state.changesTo(7) // Assuming 7 is 'Closed' state for problem
if (current.state == 7) {
    var grIncident = new GlideRecord('incident');
    grIncident.addQuery('problem_id', current.sys_id);
    grIncident.addQuery('state', '!=', 7); // Don't try to close already closed incidents
    grIncident.query();

    while (grIncident.next()) {
        grIncident.state = 7; // Set the state to Closed
        grIncident.update(); // Update the incident
    }
}

“Create Task” vs. “Catalog Task”

Workflow activities can create various types of tasks, and it’s important to understand the distinction.

Understanding: The “Create Task” workflow activity is generic; it creates records on any task-based table (e.g., Incident, Problem, Change Request, or their respective task tables like Incident Task). The “Catalog Task” activity, however, is specific to the `sc_task` table, which is designed for tasks related to service catalog requests.

Form Behavior and UI/UX: Crafting a Seamless User Experience

The user interface is where most interactions happen. Ensuring forms are intuitive, fields behave as expected, and users are guided properly is key to a good ServiceNow experience. Often, small UI glitches can lead to major user frustration.

Making Fields Mandatory or Read-Only: The Five Ways

This is a classic interview question and a core UI customization need.

Understanding: You have several tools at your disposal to control field behavior:

  1. Dictionary Properties: The most fundamental. Set a field as mandatory or read-only directly in its dictionary entry. This applies globally to all records in that table.
  2. Dictionary Overrides: Allows a field in a child table to behave differently than the same field in its parent table. For example, making `priority` mandatory on `incident` but not on `task`.
  3. UI Policies: Client-side logic to make fields mandatory, read-only, visible/hidden, or show/hide related lists based on specific conditions on a form.
  4. Data Policies: Similar to UI policies but enforce rules at both client and server sides, across all data input sources (form, import sets, web services). Cannot hide fields or control related lists/views.
  5. Client Script (`g_form.setMandatory`, `g_form.setReadOnly`): For complex, dynamic client-side logic where UI Policies don’t suffice.

Dictionary Overrides: Child Table Customization

When you extend a table, you might want a field to behave differently in the child table without affecting the parent.

Fix: Use a Dictionary Override. This allows you to modify properties like default value, mandatory status, read-only status, and choice lists for an inherited field specifically on the child table.

Important: You can only apply dictionary overrides on tables that are in the same scope as the parent table. This is a common oversight!

Reference Qualifiers: Filtering Related Data

Reference fields often need to display a subset of records, not everything.

Understanding: Reference Qualifiers restrict the data shown in reference and List type fields. There are three types:

  • Simple: A fixed query string. E.g., `active=true` to show only active users.
  • Dynamic: Uses a pre-defined “Dynamic Filter Option” (created via System Definition) that generates a query based on context. E.g., “Assigned to my groups” filter.
  • Advanced (JavaScript): Uses custom JavaScript to build a complex query string dynamically. E.g., `javascript:’assignment_group=’ + current.assignment_group + ‘^priority<3'`. This is powerful for highly contextual filtering.

Dependent Values: Cascading Dropdowns

This is crucial for creating intuitive forms where choices in one field influence choices in another, like Category and Subcategory.

Fix: In the dictionary entry of the dependent field (e.g., Subcategory), set the “Dependent field” attribute to the parent field (e.g., Category). Then, when defining choices for the dependent field, you link each choice to a specific value of the parent field.

Scenario: Imagine a “Category” field with values “Hardware,” “Software,” “Network.” You want “Subcategory” to show “Laptop, Desktop, Printer” only when “Hardware” is selected. This is a perfect use case for dependent values.

UI Policies vs. Data Policies: The Client-Server Divide

This is a prime interview question that tests your understanding of execution context.

UI Policies:

  • Client-side only.
  • Can make fields mandatory, read-only, visible/hidden.
  • Can show/hide related lists.
  • Can control views.
  • Can run client scripts (though generally avoided if UI Policy actions suffice).
  • Only apply to forms.

Data Policies:

  • Client-side and Server-side.
  • Can make fields mandatory, read-only.
  • Cannot hide fields.
  • Cannot control related lists or views.
  • Cannot run scripts.
  • Apply to all data sources (forms, import sets, web services, etc.).
Key Difference: If you need to *hide* a field or control a related list/view, you MUST use a UI Policy. If you need consistent data enforcement across *all data input channels*, use a Data Policy. Data policies are also preferred for mandatory/read-only if no visibility control is needed, as they offer server-side validation.

`setVisible` vs. `setDisplay`: The Space Conundrum

When hiding fields with client scripts, there’s a subtle but important visual difference.

Fix:

  • `g_form.setVisible(‘field_name’, false);` will hide the field but will leave an empty space where the field used to be.
  • `g_form.setDisplay(‘field_name’, false);` will hide the field AND remove the space it occupied, making the form layout reflow.

For a cleaner UI, `setDisplay` is often preferred when a field needs to be fully removed from the visual layout.

UI Macros: Custom Controls and Advanced UI

When standard fields aren’t enough, UI Macros come to the rescue.

Understanding: UI Macros are used to create custom controls and interfaces, like radio buttons or complex sections, using Jelly scripting. They enable advanced UI customizations. They are often embedded as formatters on forms.

Fix (Hiding/Showing with Script): To hide or show a UI Macro dynamically, you’ll need to use JavaScript DOM manipulation, targeting the element’s ID.

// Hiding a UI Macro
document.getElementById('uimacro_id').style.display = 'none';

// Showing a UI Macro
document.getElementById('uimacro_id').style.display = 'block';
Important: For this to work with Client Scripts, the “Isolate script” checkbox on your Client Script must be unchecked. Unchecking this can lead to conflicts with other scripts and is generally discouraged unless absolutely necessary for DOM manipulation and you understand the risks.

Attachment Management: `no_attachment` and Mandatory Attachments

Attachments are often critical, and you might need to control their presence.

Fix (`no_attachment`): To disable attachments on a form, add the `no_attachment` attribute to the “Collection” dictionary entry for that table. This effectively removes the attachment icon.

Fix (Mandatory Attachment): To make an attachment mandatory before form submission, use a Before Business Rule.

// Business Rule: Make Attachment Mandatory
// Table: (Your Table, e.g., incident)
// When: Before
// Insert: true, Update: true
(function executeRule(current, previous /*null when async*/) {
    if (current.hasAttachments() != true) {
        gs.addInfoMessage("Please add an attachment before submitting.");
        current.setAbortAction(true); // Stop the form submission
    }
})(current, previous);

Field Uniqueness

Ensuring certain fields have unique values (like employee IDs or serial numbers) is critical for data integrity.

Fix: Go to the respective field’s Dictionary entry and set the “Unique” checkbox to `true`. ServiceNow will then automatically prevent duplicate entries for that field.

`autoSysFields(false)`: Updating Without System Field Changes

Sometimes you need to update a record via script without touching `sys_updated_by`, `sys_updated_on`, etc.

Fix: Use `gr.autoSysFields(false);` before `gr.update();`. This tells ServiceNow to skip updating the system audit fields for that specific operation.

var gr = new GlideRecord('incident');
gr.get('sys_id_of_incident'); // Replace with actual sys_id of the record
if (gr.isValidRecord()) {
    gr.short_description = "Updated without touching system fields";
    gr.autoSysFields(false); // Do not update sys_updated_by, sys_updated_on, etc.
    gr.update();
    gs.info('Incident ' + gr.number + ' updated without system field changes.');
}

Scripting Gotchas and Best Practices: Writing Robust Code

Scripting is where the real power of ServiceNow lies, but it’s also where many errors occur. Understanding execution order, API usage, and best practices can prevent countless headaches.

Client Script vs. UI Policy Execution Order

This is a common source of confusion and unexpected behavior on forms.

Understanding: Client Scripts generally run first, then UI Policies. Specifically:

  1. Query Business Rules
  2. Display Business Rules
  3. `onLoad` Client Scripts
  4. `onLoad` UI Policies
  5. `onChange` Client Scripts
  6. `onChange` UI Policies
  7. `onSubmit` Client Scripts
  8. Client UI Actions
  9. Server-Side UI Actions
  10. Before Business Rules (low order)
  11. Engines (Approval, Assignment, Data Policy, Escalation, etc.)
  12. Before Business Rules (high order)
  13. Database Operation
  14. After Business Rules (low order)
  15. After Engines (Label, Listener, Notifications, etc.)
  16. Email Notifications (triggered by record change)
  17. After Business Rules (high order)
  18. Async Business Rules
Pro-tip: If a Client Script and a UI Policy conflict, the UI Policy’s changes might overwrite the Client Script’s because the UI Policy runs later. This is why it’s crucial to understand the order.

Making `onChange` Client Scripts Run `onLoad`

A smart trick for reusing `onChange` logic on form load.

Fix: An `onChange` Client Script typically has a condition like `if (isLoading || newValue === ”) { return; }`. Remove this condition, and the script’s logic will execute when the form loads, treating it as an initial “change.”

Stopping Form Submission: Client-Side vs. Server-Side

Crucial for enforcing validation and business rules.

Client-side: In an `onSubmit` Client Script, simply use `return false;` to abort the submission.

Server-side: In a `Before` Business Rule, use `current.setAbortAction(true);` to stop the database operation.

`gsftSubmit()`: Bridging Client and Server UI Actions

Sometimes a UI Action needs to do something client-side, then kick off a server-side process.

Understanding: `gsftSubmit(null, g_form.getFormElement(), ‘action_name’);` is used in a client-side UI Action script to trigger the *server-side* part of the same (or another) UI Action. The third parameter (`action_name`) is the `action_name` field value of the UI Action you want to trigger server-side.

GlideAjax: Asynchronous Communication is King

Getting server data on the client side without blocking the UI is vital for good performance.

Understanding: GlideAjax is a client-side API to send data to and receive data from the server asynchronously. It uses Script Includes on the server to process requests.

`getXML()` (Asynchronous): Sends a request and continues executing other client-side code immediately. When the response arrives, a specified callback function handles it. This is generally the preferred method for a responsive UI.

// Asynchronous GlideAjax example
var ga = new GlideAjax('YourScriptInclude'); // Name of your Script Include
ga.addParam('sysparm_name', 'yourFunction'); // Name of the function in Script Include
ga.addParam('sysparm_caller_sys_id', g_form.getValue('caller_id')); // Pass client-side data
ga.getXML(callbackFunction); // Call function when response comes back

function callbackFunction(response) {
    var answer = response.responseXML.documentElement.getAttribute("answer");
    g_form.setValue('some_field', answer); // Use the server's response on the client
    gs.info('Received answer asynchronously: ' + answer);
}

`getXMLWait()` (Synchronous): Sends a request and *waits* for the server’s response before any other client-side code can execute. This blocks the user interface and can lead to a poor user experience if the server response is slow.

// Synchronous GlideAjax example - Use sparingly!
var ga = new GlideAjax('HelloWorld');
ga.addParam('sysparm_name', 'helloWorld');
ga.addParam('sysparm_user_name', "Bob");
ga.getXMLWait(); // Waits here until server responds, blocking UI
var answer = ga.getAnswer(); // Get the response
alert(answer); // Only after response is received
gs.info('Received answer synchronously: ' + answer);
Interview Scenario: “When caller selected is Contractor, the form submission should be stopped.” For this specific scenario, a synchronous call *might* be used if the validation absolutely *must* happen before the form proceeds, and you are willing to risk a momentary block. However, an `onSubmit` Client Script with `return false;` after an asynchronous GlideAjax call (with a global variable to track validation status) is often a better pattern for user experience.

`GlideRecord` in Client Scripts: A Performance Killer

This is a common mistake for new developers.

Fix & Best Practice: Never use `GlideRecord` directly in Client Scripts. `GlideRecord` is a server-side API. Attempting to use it in a client script will either fail or, if somehow made to work (e.g., in a non-isolated script using `g_server.runScript` – which is a bad practice), will result in synchronous server calls, severely impacting form performance and user experience. Always use `GlideAjax` for server communication from the client.

Display Business Rules (`g_scratchpad`)

A powerful way to get server-side data to your client scripts `onLoad`.

Understanding: A Display Business Rule runs on the server before the form is displayed to the user and before any client scripts or UI policies execute. Its primary objective is to populate the `g_scratchpad` object with server-side data, which is then sent to the client. Client scripts can then access this `g_scratchpad` data without needing a separate `GlideAjax` call.

// Example Display Business Rule
// Table: Incident
// When: Display
(function executeRule(current, previous /*null when async*/) {
    g_scratchpad.callerVIP = current.caller_id.vip; // Get VIP status from server
    // Example: Count related tasks from server side
    var grTaskCount = new GlideAggregate('incident_task');
    grTaskCount.addQuery('incident', current.sys_id);
    grTaskCount.addAggregate('COUNT');
    grTaskCount.query();
    if(grTaskCount.next()){
        g_scratchpad.relatedTasksCount = grTaskCount.getAggregate('COUNT');
    } else {
        g_scratchpad.relatedTasksCount = 0;
    }
})(current, previous);

// Example Client Script (onLoad)
// Table: Incident
// Type: onLoad
function onLoad() {
    if (g_scratchpad.callerVIP == 'true') {
        g_form.addInfoMessage('Caller is a VIP!');
    }
    if (g_scratchpad.relatedTasksCount > 0) {
        g_form.addInfoMessage('This incident has ' + g_scratchpad.relatedTasksCount + ' related tasks.');
    }
}

`previous` Object in Async Business Rules

The `previous` object holds the record’s values *before* the current save operation.

Understanding: You cannot use the `previous` object in Async Business Rules. Async BRs run asynchronously in the background via a scheduled job, and by the time they execute, the `previous` state of the record is no longer reliably available in that context. It’s only available in `Before` and `After` synchronous Business Rules.

`GlideAggregate`: Aggregating Data Efficiently

When you need to count, sum, average, or find min/max values, `GlideRecord` isn’t always the best choice.

Fix & Best Practice: Use `GlideAggregate` (an extension of `GlideRecord`) for all aggregation queries. It’s designed for this and is much more efficient than using `getRowCount()` and iterating through records. It supports `COUNT`, `SUM`, `MIN`, `MAX`, `AVG`.

// Efficiently count active incidents using GlideAggregate
var grAgg = new GlideAggregate('incident');
grAgg.addQuery('active', true);
grAgg.addAggregate('COUNT');
grAgg.query();
var activeIncidents = 0;
if (grAgg.next()) {
    activeIncidents = grAgg.getAggregate('COUNT');
    gs.info('Active incident count: ' + activeIncidents);
}

Troubleshooting Tip: If you’re using `getRowCount()` in a server script and notice performance issues, especially on large tables, consider refactoring to `GlideAggregate`.

Identifying and Deleting Duplicate Records

Duplicate data is a common headache. Here’s how to find and clean it up using scripts.

Identifying Duplicates:

var gr = new GlideAggregate('incident'); // Replace 'incident' with your table
gr.addAggregate('COUNT', 'number'); // Group by a field that might be duplicated
gr.addHaving('COUNT', '>', 1); // Only show groups with more than 1
gr.groupBy('number'); // Group by that field
gr.query();
while (gr.next()) {
    var duplicateValue = gr.getValue('number');
    var count = gr.getAggregate('COUNT', 'number');
    gs.print('Found ' + count + ' duplicates for number: ' + duplicateValue);

    // Now find the actual duplicate records
    var duplicateRecords = new GlideRecord('incident');
    duplicateRecords.addQuery('number', duplicateValue);
    duplicateRecords.orderBy('sys_created_on'); // Order to keep the oldest/newest
    duplicateRecords.query();
    while (duplicateRecords.next()) {
        gs.print('  - Duplicate Sys ID: ' + duplicateRecords.getValue('sys_id') + ', Created On: ' + duplicateRecords.getValue('sys_created_on'));
    }
}

Deleting Duplicates (Keep one, delete others):

var testGr = new GlideAggregate('u_my_table'); // Replace with your table name
testGr.groupBy('u_duplicate_column'); // Column with potential duplicates (e.g., email address)
testGr.query();

while (testGr.next()) {
    var duplicateValue = testGr.getValue('u_duplicate_column');
    var duplicateRecords = new GlideRecord('u_my_table');
    duplicateRecords.addQuery('u_duplicate_column', duplicateValue);
    duplicateRecords.orderBy('sys_created_on'); // Keep the oldest, delete newer ones
    duplicateRecords.query();

    if (duplicateRecords.next()) { // Keep the first (oldest) record
        gs.print('Keeping: ' + duplicateRecords.sys_id + ' for value ' + duplicateValue);
        while (duplicateRecords.next()) { // Delete subsequent (newer) duplicates
            gs.print('Deleting: ' + duplicateRecords.sys_id + ' for value ' + duplicateValue);
            duplicateRecords.deleteRecord();
        }
    }
}
gs.info('Duplicate deletion script completed.');
Caution: Always run deletion scripts in a non-production instance first, and ideally with `gs.print()` statements before actual `deleteRecord()` calls, to verify what would be deleted. Use the “Record for Rollback?” option when running background scripts to have a safety net.

Development Lifecycle and Deployment: Mastering Update Sets

Moving changes from development to testing and then to production is a daily task for ServiceNow developers. Update sets are the vehicle for this, but they come with their own set of rules and potential issues.

What Update Sets Capture (and What They Don’t)

A common misconception is that update sets capture *everything*. They don’t.

Understanding: Update sets capture configuration changes (e.g., tables, fields, form layouts, business rules, client scripts, workflows, system properties). They generally do NOT capture transactional data (e.g., Incidents, Users, Groups, actual records). The key indicator is the `update_synch=true` attribute on a table – if present, changes to that table’s configuration are captured.

Interview Question: “Which configurations get captured in update sets and which are not?” Be ready with examples!

Handling Preview Errors: “Skipping” vs. “Accepting”

When you preview an update set, you might encounter conflicts. It’s how you resolve them that matters.

Fix:

  • “Skip Remote Update”: Choose this if you want to keep the change that exists in your current target instance, ignoring the conflicting change from the incoming update set.
  • “Accept Remote Update”: Choose this if you want to apply the change from the incoming update set, overwriting the conflicting change in your target instance.

Common preview errors include “Found a local update that is newer than this one” or “Could not find a record in sys_scope for column sys_scope referenced in this update” (often due to missing applications or scoped app changes). Always analyze carefully before choosing!

Update Set Best Practices (and Common Mistakes)

Good update set hygiene is crucial for smooth deployments.

  • Never Delete Updates: If you realize an update isn’t needed, either back it out or just don’t commit it. Don’t delete individual updates from an update set, as this can break referential integrity.
  • Keep Update Sets Small: Large update sets are harder to manage, review, and troubleshoot. Aim for logical, self-contained units of work.
  • Publish Workflows: Always publish your workflow changes *before* completing the update set. Unpublished workflows won’t be captured correctly.
  • Naming Conventions: Use a consistent naming convention (e.g., `INC1234567_FeatureName_Description_v1`). Include story/ticket numbers.
  • Specify Order: If multiple update sets have dependencies, ensure they are imported and committed in the correct order.
  • Don’t Merge Update Sets (Generally): While merging exists, it can create complex versioning issues and make rollback difficult. Batch update sets are a better alternative for grouping.
  • Batch Update Sets: Create a parent update set and link child update sets to it. Closing the parent closes children, and moving/previewing/committing the parent processes all children automatically. This is ideal for related but distinct changes.
  • Backout Update Set: If you commit an update set and later realize the changes are incorrect or detrimental, you can “Back Out” the update set to revert its changes. This is a powerful safety net.
  • Moving Uncaptured Development: For items not captured (like data records or scheduled jobs by default), you can export/import XML files manually or use scripts to capture specific records into an update set.

Service Catalog and Request Fulfillment: Streamlining Service Delivery

The Service Catalog is often the front door for users to interact with IT. Configuring it effectively involves understanding Catalog Items, Record Producers, Order Guides, and their associated scripts.

Record Producer vs. Catalog Item

A fundamental distinction that defines how user requests are processed.

Record Producer: Used to gather information and create a record directly in a ServiceNow table (e.g., create an Incident, Problem, Change Request, or a custom record). It bypasses the request/request item/task fulfillment process. It’s for “gathering open-ended records.”

Catalog Item: Used for requesting a service or item that requires an approval and fulfillment workflow. It typically generates a Request (REQ), a Requested Item (RITM), and potentially multiple Catalog Tasks (SCTASKs). It’s for “requesting a service.”

Scenario: “If you’re offering end-users the ability to request a service where you need to control approvals and fulfillment, you will use a catalog item. If you’re just looking to gather information or need to gather open-ended records from your users, you will use a record producer.”

Workflow on Record Producers

Can a Record Producer have a workflow?

Understanding: You cannot directly attach a workflow to a Record Producer itself. The workflow is attached to the target record that the Record Producer creates (e.g., an Incident workflow if it creates an Incident). This workflow then dictates the lifecycle of that created record.

Order Guide vs. Catalog Item

For bundling services or items, Order Guides are invaluable.

Understanding:

  • Catalog Item: A single, individual service or item (e.g., “Request a Laptop”). Creates one RITM.
  • Order Guide: A “collection” of related catalog items, bundled together to simplify ordering for the user (e.g., “New Employee Onboarding” guide which includes a Laptop, Software Access, Phone, etc.). It can have a rule base to show/hide items and variables dynamically, and it can create multiple RITMs under a single REQ.

Order guides are excellent for streamlining complex requests that involve multiple fulfillments, presenting them as a single cohesive process to the user.

Client APIs in Record Producer Scripts

Record Producers have both a client-side (variables on the form) and a server-side (the “Script” field) component.

Fix: You cannot write client-side APIs (like `g_form` or `g_user`) in the Record Producer’s server-side script. The “Script” field in a Record Producer executes on the server after the form is submitted. Use server-side APIs (`current`, `gs`, `GlideRecord`, etc.) here.

Cascade Variables in Order Guides

Keeping variable values consistent across multiple items in an order guide.

Fix: The “Cascade Variables” checkbox in an Order Guide allows variables with the same name in the Order Guide and its contained Catalog Items to automatically pass their values down. This prevents users from re-entering the same information multiple times.

Variable Sets: Reusability for Catalog Items

Don’t repeat yourself! Variable Sets are a developer’s best friend in the Service Catalog.

Best Practice: Use Variable Sets to group common variables (questions) that appear across multiple Catalog Items. This saves development time, ensures consistency, and simplifies maintenance (change it once in the variable set, and it updates everywhere).

Catalog UI Policy vs. Normal UI Policy

Specifics for Catalog forms.

Catalog UI Policies: Work on Catalog Items, Requested Items, and Catalog Task forms. They *do not* control views. They have an “Applies On” section (Catalog Item, Requested Item, Catalog Task, Record Producer) to specify where they run.

Normal UI Policies: Work on standard forms and can control views.

Fix for Record Producer UI Policy: To make a Catalog UI Policy work *only* on a Record Producer, check the “Applies on Target Record” checkbox. (Note: This refers to the record *created* by the Record Producer, not the producer form itself).

Fix for Catalog Task UI Policy: To make a Catalog UI Policy work *only* on Catalog Tasks, check the “Applies on Catalog Task” checkbox.

Catalog Client Script vs. Normal Client Script

Similar to UI Policies, client scripts also have catalog-specific behaviors.

Catalog Client Scripts: Have 3 types (`onLoad`, `onChange`, `onSubmit`). They work on Catalog Items, Requested Items, and Catalog Tasks. They *do not* work on views.

Normal Client Scripts: Have 4 types (`onLoad`, `onChange`, `onSubmit`, `onCellEdit`). They work on standard forms and can be applied to specific views.

Workflow Activities: Branch, Join, Turnstile, Rollback

Orchestrating complex processes often requires specific workflow activities.

  • Branch: Allows multiple transitions from a single activity, effectively creating parallel paths.
  • Join: Merges multiple parallel transitions back into a single path. Often used with `Wait for condition` or `Wait for workflow` to ensure all branches are complete before proceeding.
  • Turnstile: Limits how many times a workflow can pass through a specific point, preventing infinite loops.
  • Rollback To: Moves the workflow backward to a specified activity, often used in error handling or re-processing.

Known Issue with Parallel Activities: If you use parallel activities without a `Join` or appropriate `Wait` conditions, the workflow might not wait for all branches to complete before moving on, leading to incomplete processes. Always use `Join` when you need all parallel paths to converge.

Workflow vs. Flow Designer: The Modern Approach

ServiceNow is pushing Flow Designer as the future of process automation.

Understanding: Flow Designer is designed to be a unified platform for creating and managing business logic, effectively replacing or enhancing many traditional components like:

  • Business Rules (for certain logic)
  • Scheduled Jobs (for background tasks)
  • Email Notifications (as part of a flow)
  • Inbound Email Actions
  • Workflows (as a more intuitive, low-code/no-code visual builder)
  • Integration Hub (for external integrations)

Flow Designer offers a more visual, maintainable, and often performant way to build automations, particularly for non-scripting experts. It’s the preferred low-code solution.

Passing Values from Main Workflow to Subflow

A common need when modularizing workflows.

Fix: In the subflow, create “Inputs” variables. In the parent workflow’s “Subflow” activity, you can map parent workflow variables to these subflow inputs. Inside the subflow, access these values using `workflow.inputs.variable_name`.

Workflow Issue Faced: “Unable to pass values from main workflow to child workflow when the child workflow was on the `sc_req_item` table. The workaround was to temporarily change the child workflow’s table to a generic one (like `global`), create the inputs, and then change it back to `sc_req_item`. This is a known platform quirk that sometimes arises with specific table contexts.”

Miscellaneous Troubleshooting & Best Practices

Finding All Components of an Application (Plugin)

When you install an application or plugin, how do you see all its moving parts?

Fix:

  1. Navigate to `sys_metadata.list` in the filter navigator.
  2. Personalize the list (gear icon) and add the “Package” column.
  3. Filter the list: `[Package] [Is] []`.
  4. Group by “Class” to see all tables, modules, script includes, etc., associated with that package.

Extending Tables After Creation

You can’t change a table’s extension status after it’s been created.

Understanding: You can only define whether a table extends another table (or is extended by another table) during the initial table creation process. Once created, this relationship cannot be changed. Plan your table hierarchy carefully from the start!

Deleting Base System Tables

A critical safeguard for instance stability.

Understanding: Base system tables (e.g., `sys_user`, `incident`, `cmdb_ci`) cannot be deleted. If you were to somehow remove one, it would be recreated during the next instance upgrade, ensuring system integrity. This prevents catastrophic data loss or system malfunction.

Assignment Rule vs. Data Lookup Rule

Two ways to automate field population, but with key differences.

Assignment Rules: Specifically for assigning tasks (e.g., Incident to a group/user). They cannot overwrite existing assignments or apply to unsaved changes.

Data Lookup Rules: More generic. They can change *any* field value (not just assignments) and can apply to unsaved changes on a form. They *can* override default values or previously set values, making them more powerful for dynamic form updates.

User Presence: Disabling a Global Feature

The “User Presence” feature shows who else is viewing a record, which some organizations prefer to disable.

Fix: Navigate to `sys_properties.list`, find the property named `glide.ui.presence.disabled`, and set its value to `true`.

`gs.include(‘validators’)`

A handy Script Include for common data validations.

Understanding: `gs.include(‘validators’);` gives you access to a Script Include called “Validators” which provides functions like `isNumeric()`, `isInteger()`, `containsOnlyChars()`, etc. This is useful for validating user input in server-side scripts.

gs.include('validators'); // Include the Validators script include

// Example usage in a Business Rule
if (email.body.priority != undefined && isNumeric(email.body.priority)) {
    // Process priority as a number
    current.priority = email.body.priority;
} else {
    gs.error('Invalid priority value received from email.');
}

Best Practices: Client-Side and Server-Side

A quick recap of essential development habits.

Client-Side Best Practices:

  • Enclose code in functions.
  • Avoid direct DOM manipulation; use `g_form` object.
  • Avoid global client scripting.
  • Do not use `g_form.getReference()` (it’s synchronous); use `GlideAjax` instead.
  • Avoid too many `alert()` statements; use `gs.addInfoMessage()` or `console.log()`.
  • Favor asynchronous calls (`GlideAjax`) over synchronous ones (`getXMLWait`).
  • Use `g_form.setDisplay()` over `g_form.setVisible()` for cleaner UI removal.

Server-Side Best Practices:

  • Use descriptive variable and function names.
  • Avoid `current.update()` in `Before` Business Rules; `current` will be saved automatically.
  • If `current.update()` is necessary in an `After` Business Rule, use `current.setWorkflow(false);` first to prevent recursive triggers.
  • Do not use synchronous calls unless absolutely critical; prefer `Async` Business Rules or scheduled jobs for long-running processes.
  • Move complex conditions out of the script field into the “Condition” field of Business Rules, UI Policies, etc.
  • Remove `gs.log()`/`gs.print()` statements after debugging.
  • Use functions for repetitive logic.
  • Prefer Flow Designer over complex scripting where possible (low-code first).
  • Add roles to groups, not individual users.
  • Comment your code thoroughly.
  • Use `GlideAggregate` for aggregations, not `getRowCount()`.
  • Use `GlideRecordSecure` for CRUD operations where ACL enforcement is needed.
  • Use `setLimit(n)` on `GlideRecord` queries to prevent processing too many records.
  • Verify values exist before using them.
  • Use System Properties instead of hardcoded values.
  • Use Messages (System UI > Messages) instead of hardcoding `gs.addInfoMessage()` strings for easy internationalization and management.

`setWorkflow(false)` and `setForceUpdate()`

Two essential `GlideRecord` methods for fine-grained control.

`setWorkflow(false)`: Prevents Business Rules and Workflow engines from running when the record is updated. Useful when you’re doing a programmatic update that shouldn’t trigger normal automation.

`setForceUpdate(true)`: Forces an update to the record even if no field values have changed. Useful for ensuring a record’s `sys_updated_on` timestamp is updated, or to trigger `After` Business Rules even when nothing visible changed.

var gr = new GlideRecord('incident');
gr.get('sys_id_of_incident'); // Replace with actual sys_id
if (gr.isValidRecord()) {
    // If you only want to update sys_updated_on without changing data or triggering BRs
    gr.setWorkflow(false); // Do not run business rules or workflows
    gr.autoSysFields(true); // Ensure sys_updated_on is updated if you need it updated without other fields
    gr.setForceUpdate(true); // Force the update
    gr.update();
    gs.info('Record ' + gr.number + ' updated with setForceUpdate and setWorkflow(false).');
}

Conclusion

ServiceNow is a vast and ever-evolving platform, and mastering its nuances takes time and experience. By understanding these common errors, their underlying causes, and the best practices for fixing them, you’re not just patching problems; you’re building a more robust, efficient, and user-friendly instance.

Remember, every error is a learning opportunity. The ability to troubleshoot effectively, coupled with a deep understanding of ServiceNow’s architecture and execution order, will transform you from a reactive fixer to a proactive architect of solutions. Keep these insights handy, continue to learn, and happy (and error-free) developing!


Scroll to Top