The Problem in One Sentence
A field is read-only on form load, and you can’t find why — neither the UI Policy nor the Client Script alone says it should be. They’re both firing, in an order you didn’t expect.
Execution Order, Briefly
On form load, ServiceNow runs (in this order):
- UI Policies whose conditions match the current record.
- onLoad Client Scripts.
- UI Policies again, after the script has run, in case a script changed a field referenced in the policy condition.
That third pass is the surprise. A Client Script that sets g_form.setValue('priority', 1) to override a default can flip a UI Policy from “no match” to “match,” and the policy then re-fires its actions — usually making the field read-only or mandatory in a way that wasn’t there a millisecond ago.
This is documented, but the docs bury it. The practical effect: scripts that look correct produce field state that looks impossible.
A Concrete Scenario
A team wants the assigned_to field on Incident to be:
- Mandatory when
priorityis 1 or 2. - Pre-populated for the current user on form load.
They write a Client Script that pre-populates assigned_to onLoad. They write a UI Policy that makes assigned_to mandatory when priority is 1 or 2.
On a P3 incident, the user opens the form. The Client Script populates assigned_to. So far so good. But the form was opened with priority blank. The UI Policy on initial load said “priority is not 1 or 2, so don’t make assigned_to mandatory.” A second UI Policy then sets priority to 3 as a default. The third UI Policy pass re-evaluates — priority is now 3, not 1 or 2, so no change. The Client Script’s pre-populated value stays.
Same story on a P1 incident, but the user-set priority is 1. The UI Policy fires, the field is now mandatory, and the pre-populated value is fine. No bug.
Now flip the scenario: the UI Policy is set to make assigned_to read-only when priority is 1 or 2 (it’s not “mandatory and unset” but rather “managers cannot reassign”). The user opens a P1. The Client Script tries to set assigned_to to the current user. The UI Policy has marked the field read-only on load. The set call silently fails. The user clicks Submit. The field is empty. No error. No clue.
That is the bug 70% of “field won’t populate” tickets resolve to.
The Rule of Thumb That Works
Pick one of these two postures and apply it consistently:
Posture A: UI Policy owns state. Client Script reads state. UI Policies set read-only, mandatory, and visibility. Client Scripts only respond to fields that policies haven’t locked. This is the maintainable choice for 80% of forms.
Posture B: Client Script owns state. UI Policy is for users only. Use UI Policies only for user-facing visibility changes; do all read-only/mandatory logic in scripts. Higher complexity, more flexible. Pick this only when policies can’t express the logic.
Mixing the two — letting a policy AND a script both write the same field property — is where the bugs live.
How to Diagnose When You’re Already In It
ServiceNow’s Client Script Debugger lets you set a breakpoint on the form. Enable it, reload, and step through the events in the timeline panel. You’ll see UI Policy firings interleaved with script firings. The script that “should have run” usually shows up in the timeline — but its setValue is followed by a UI Policy that reverts the change.
If you don’t want to attach a debugger, drop a console.log('script run at ' + new Date()) in the Client Script and watch the console. Then add a gs.log line in the policy’s script-action equivalent. The order in the logs tells you who’s overwriting whom.
A Pattern That Avoids the Trap
For “pre-populate on load if empty” logic, gate the Client Script on the field’s current writable state:
function onLoad() {
const el = g_form.getElement('assigned_to');
if (el && !el.disabled && !g_form.getValue('assigned_to')) {
g_form.setValue('assigned_to', g_user.userID);
}
}
Reading the DOM element’s disabled property catches the case where a policy has locked the field. The script no-ops cleanly instead of silently failing.
What to Do This Week
Pull the top five forms in your instance by user complaint volume. For each, list every UI Policy and Client Script that touches the same field. If any field is written by both, pick Posture A or Posture B and refactor. Almost every “phantom field” ticket goes away once the boundary is clean.