ServiceNow setValue vs setDisplayValue: Complete Developer Guide

One of the most common data corruption bugs in ServiceNow comes from a single misunderstanding: passing a display label to setValue() instead of the actual stored value. It saves successfully, throws no errors, and breaks everything downstream — silently.

If you have ever built a Business Rule, a Script Include, or a scripted integration in ServiceNow, you have almost certainly used setValue(). It is the primary method for setting field values programmatically on GlideRecord objects. What most developers do not fully understand is the distinction between setValue() and setDisplayValue() — and that distinction is the difference between clean, reliable data and a corrupted record that silently breaks workflows, reports, and SLAs.

This guide covers everything: how each method works internally, exactly when to use each one, the field types where the confusion causes the most damage, how to audit your existing scripts, and patterns for handling incoming data from external systems where you may not control the format.

How ServiceNow Stores Field Values

To understand setValue() and setDisplayValue(), you first need to understand that ServiceNow stores two representations of most field values: the stored value and the display value. They are different things, and the database only stores one of them.

Take the state field on the Incident table. When a user looks at an incident in the browser, they see labels like "New", "In Progress", "On Hold", "Resolved", "Closed". These are display values — human-readable labels that ServiceNow translates from a choice list. What the database actually stores is an integer: 1 for New, 2 for In Progress, 3 for On Hold, 6 for Resolved, 7 for Closed.

Reference fields work the same way but in reverse. A field like assigned_to displays a user's full name — "John Smith" — but what the database stores is the sys_id of that user record: a 32-character GUID like a8f98bb0eb32010045e1a5115206fe3a. The display value is the name. The stored value is the sys_id.

This distinction is fundamental. When you call gr.setValue('state', someValue), ServiceNow writes someValue directly to the database column — exactly as you pass it, with no translation. When you call gr.setDisplayValue('state', someValue), ServiceNow first translates someValue from its display representation to the correct stored value, then writes that to the database.

setValue() — What It Does and When to Use It

The setValue(fieldName, value) method sets a field to the value you pass, verbatim. It is the right choice whenever you already know the stored value and want to write it directly.

For choice fields like state, priority, urgency, and impact, the stored values are integers defined in the choice list. You can look these up in ServiceNow by navigating to the field's choice list configuration, or by querying an existing record and calling gr.getValue('state') — which returns the stored integer, not the display label.

// Setting state on an incident — correct usage of setValue()
var gr = new GlideRecord('incident');
gr.get(incidentSysId);

gr.setValue('state', 2);       // In Progress
gr.setValue('priority', 2);    // High
gr.setValue('urgency', 1);     // 1 - High
gr.setValue('impact', 2);      // 2 - Medium
gr.update();

For reference fields, the stored value is always a sys_id. If you are setting assigned_to, you pass the sys_id of the user, not their name. If you have already retrieved the user's GlideRecord elsewhere in your script, you pass userGr.sys_id.toString().

// Setting a reference field with setValue() — pass the sys_id
var gr = new GlideRecord('incident');
gr.get(incidentSysId);

var userSysId = gs.getUserID(); // current user's sys_id
gr.setValue('assigned_to', userSysId);
gr.update();

For boolean fields, setValue() accepts true or false as JavaScript booleans, or the strings 'true' and 'false'. For string fields, plain text, dates, and numbers, you pass the value as-is. The key rule is: setValue() does no translation. You are responsible for passing the correct stored representation.

setDisplayValue() — What It Does and When to Use It

The setDisplayValue(fieldName, value) method accepts a display label and resolves it to the correct stored value before writing to the database. It is the right choice when you have a display label and want ServiceNow to handle the translation for you.

For choice fields, setDisplayValue() looks up the label in the choice list and stores the corresponding key. For reference fields, it performs a query to find the record matching the display value — which for most reference fields is the name or number field of the referenced table.

// setDisplayValue() resolves labels to stored values
var gr = new GlideRecord('incident');
gr.get(incidentSysId);

gr.setDisplayValue('state', 'In Progress');   // stores 2
gr.setDisplayValue('priority', 'High');        // stores 2
gr.setDisplayValue('assigned_to', 'John Smith'); // looks up sys_id
gr.update();

setDisplayValue() is particularly useful when you are processing data from external systems — a REST payload, a CSV import, or a webhook — where the incoming data uses human-readable labels rather than stored keys. Rather than building a lookup table yourself, you can let ServiceNow do the resolution.

That said, setDisplayValue() on reference fields fires an additional database query to resolve the display value to a sys_id. At scale — inside a loop processing thousands of records — this adds up. Where performance matters, look up the sys_id once and use setValue() instead. The gs object reference and GlideRecord performance tips article cover this pattern in detail.

The Silent Bug: Passing Display Labels to setValue()

Here is where the real damage happens. When you pass a display label — a string like 'In Progress' — to setValue() on a choice field, ServiceNow does not throw an error. It does not warn you. It writes the string 'In Progress' directly to the database column that is supposed to contain an integer.

// This saves successfully and corrupts your data
var gr = new GlideRecord('incident');
gr.get(incidentSysId);
gr.setValue('state', 'In Progress'); // writes the string, not 2
gr.update();
// No error. Record appears to save correctly.

The record updates. The state field shows "In Progress" in the UI — because ServiceNow is displaying the raw value and it happens to match a label string. But the underlying data is wrong. The column now contains the string 'In Progress' instead of the integer 2.

This breaks immediately downstream. Any workflow condition checking state is 2 will never match — because the field now contains a string, not the integer the condition expects. Any report filter on state will return incorrect counts. Any SLA definition that triggers when state changes to In Progress will silently skip affected records. Business rules with encoded queries like state=2 will not fire on those records.

The worst part is that the bug can persist undetected for days or weeks. The UI looks correct. No script errors appear in the log. The only signal is that downstream automations stop working — and tracing that back to a setValue() call with the wrong argument type requires deliberate investigation.

Choice Field Stored Values — Where to Find Them

For any choice field, you can look up the stored values directly in ServiceNow. Navigate to System Definition → Choice Lists and search by table and field name. The "Value" column shows what gets stored in the database; the "Label" column shows what users see on screen.

For the Incident state field specifically, the standard stored values are: 1 (New), 2 (In Progress), 3 (On Hold), 6 (Resolved), 7 (Closed), 8 (Cancelled). Note that 4 and 5 are intentionally skipped in the standard configuration — which catches developers who guess sequentially rather than looking up the actual values.

You can also retrieve the stored value of an existing record programmatically using gr.getValue('fieldName') — which always returns the stored value, never the display label. This is the counterpart to gr.getDisplayValue('fieldName'), which returns the human-readable label. Keeping this symmetry in mind — getValue/setValue work with stored values, getDisplayValue/setDisplayValue work with display labels — prevents most confusion.

// Always use getValue to retrieve stored values for debugging
var gr = new GlideRecord('incident');
gr.get(incidentSysId);

gs.log(gr.getValue('state'));        // logs "2"
gs.log(gr.getDisplayValue('state')); // logs "In Progress"

// These two are equivalent when writing:
gr.setValue('state', 2);
gr.setDisplayValue('state', 'In Progress');

Reference Fields: The sys_id Rule

For reference fields, the rule is simple: setValue() always takes a sys_id. If you do not have the sys_id, use setDisplayValue() and let ServiceNow resolve it — but be aware of the performance cost for high-volume scripts.

A common pattern in integrations is receiving a username or user email from an external system and needing to set a reference field like caller_id or assigned_to. The safest approach is to look up the sys_id once, then use setValue():

// Resolve display value to sys_id once, then use setValue()
function getUserSysId(username) {
    var user = new GlideRecord('sys_user');
    user.addQuery('user_name', username);
    user.setLimit(1);
    user.query();
    if (user.next()) {
        return user.sys_id.toString();
    }
    return null;
}

var gr = new GlideRecord('incident');
gr.get(incidentSysId);

var assigneeSysId = getUserSysId('jsmith');
if (assigneeSysId) {
    gr.setValue('assigned_to', assigneeSysId);
    gr.update();
}

This approach is preferable to calling setDisplayValue() with the username in a loop, because you control exactly when the lookup happens and can handle the null case explicitly. The Script Includes guide covers how to build reusable lookup utilities like this as callable server-side functions.

Boolean and Date Fields

Boolean fields in ServiceNow store true or false. Pass JavaScript booleans or the string equivalents to setValue(). setDisplayValue() on boolean fields accepts 'Yes'/'No' or 'True'/'False' and resolves them accordingly — though passing the actual boolean is cleaner and more explicit.

// Boolean fields
gr.setValue('active', true);
gr.setValue('active', false);

// Date fields — pass in the ServiceNow internal date format
// yyyy-MM-dd HH:mm:ss (UTC)
gr.setValue('due_date', '2026-06-30 17:00:00');

// Or use GlideDateTime for reliable formatting
var gdt = new GlideDateTime();
gdt.addDaysLocalTime(7);
gr.setValue('due_date', gdt.getValue());

Date fields are worth special attention in integrations. External systems often pass dates in formats that differ from ServiceNow's internal format — ISO 8601, Unix timestamps, or locale-specific date strings. Always convert to ServiceNow's internal format using GlideDateTime before calling setValue(). Passing a date string in the wrong format to setValue() will either set the field to a wrong value or blank it out, depending on how ServiceNow parses it.

Auditing Your Scripts

If you want to audit your existing instance for setValue() misuse, the search pattern is straightforward. In your Script editor or in Studio, search for setValue( across Business Rules, Script Includes, and Scheduled Jobs. Look for any call where the second argument is a quoted string being passed to a field that is a choice field or reference field.

The highest-risk locations are Business Rules that run on high-volume tables like incident, task, and sc_req_item, and Scripted REST API endpoints that receive display values from external callers. The debugging ServiceNow scripts guide covers how to use the Script Debugger and gs.log() to trace field values during execution and confirm what is actually being written to the database.

For any suspect setValue() call you find, the fix is one of two things: replace the display label with the correct stored value, or change the call to setDisplayValue(). Both produce the same result — correct data in the database. The choice between them depends on whether you have the stored value available or only the display label.

Import Sets and Transform Maps

Import Sets deserve special mention because they are a common source of setValue() confusion. When you define a Transform Map and write transform scripts, the source data from the import table typically contains display values — the raw strings from your CSV, Excel file, or API payload. A common mistake is using setValue() to copy those raw strings directly to the target table fields without transformation.

// In a Transform Map script — incorrect
answer = source.u_state; // contains 'In Progress' as a string
target.setValue('state', answer); // writes string, not integer

// Correct approach
target.setDisplayValue('state', source.u_state); // resolves to integer
// OR
var stateMap = {'New': 1, 'In Progress': 2, 'On Hold': 3, 'Resolved': 6, 'Closed': 7};
target.setValue('state', stateMap[source.u_state] || 1);

The explicit mapping approach is more robust for production imports because it handles label mismatches — misspellings, different capitalisation, or labels from a source system that differ from your ServiceNow choice list — in a controlled way rather than silently writing garbage data.

Quick Reference

To summarise the rules clearly:

Use setValue() when: you have the stored value — an integer for choice fields, a sys_id for reference fields, a boolean for boolean fields, or a properly-formatted date string for date fields.

Use setDisplayValue() when: you have a display label — a human-readable string — and want ServiceNow to resolve it to the stored value. Useful for external integrations, import processing, and cases where you only have the label available.

Never pass a display label to setValue() on choice or reference fields. It saves silently and corrupts your data.

The getValue()/getDisplayValue() symmetry applies in reverse when reading: getValue() returns the stored value, getDisplayValue() returns the human-readable label. Use the pair that matches what you need downstream. If you are writing to another setValue() call, use getValue(). If you are displaying to a user or logging for human readability, use getDisplayValue().

NowSpectrum Resource

ServiceNow Scripting Fundamentals Reference

A complete reference for server-side scripting in ServiceNow — GlideRecord, GlideSystem, Script Includes, Business Rules, and more. Built for working developers, not certification prep.

Get the Reference →
← Back to all posts