Selva Arun
Mega Sage

Real World Example: Number and Work Notes Fields Not Visible for Non-Admin Users on Tables Extending Task

Applies To: Any table extending the Task table | Platform Security | ACLs
Tested On: Zurich


I searched the community before writing this. There are posts about work notes visibility and ACL inheritance as separate topics, but nothing that walks through the full diagnosis end-to-end — the combination of admin_overrides, missing field-level read ACLs, and parent table inheritance all contributing to the same symptom. This article fills that gap. It is not a duplicate.


Situation

A non-admin user could not see the Number field or the Work Notes field on records from a custom table that extends the Task table. The user had the correct role and could open records — those two fields were simply missing. Admins could see everything normally.


Task

Three questions needed answering:

  1. Why are Number and Work Notes not visible to non-admin users?
  2. What ACL configuration is causing it?
  3. How do you fix it without breaking anything else?

Action

Step 1 — Audit all ACLs on your table (read-only script)

Before changing anything, inspect every ACL on your table. This script only reads — it makes no changes. Run it from System Definition → Scripts - Background:

// READ ONLY — Safe ACL Inspector
// No changes made

var tableName = 'your_table_name'; // <-- replace with your table

var gr = new GlideRecord('sys_security_acl');
gr.addQuery('name', 'STARTSWITH', tableName);
gr.orderBy('name');
gr.query();

gs.info('========== ACLs on: ' + tableName + ' ==========');

while (gr.next()) {
    gs.info(
        '[ACL] '              + gr.getValue('name')                 +
        ' | Op: '             + gr.getValue('operation')            +
        ' | Active: '         + gr.getValue('active')               +
        ' | Admin overrides: '+ gr.getValue('admin_overrides')      +
        ' | Roles: '          + (gr.getValue('roles') || 'NONE')    +
        ' | Created by: '     + gr.getValue('sys_created_by')       +
        ' | sys_id: '         + gr.getValue('sys_id')
    );
}

gs.info('========== Done — no changes made ==========');

Step 2 — Check parent table ACLs for the specific fields (read-only script)

Because your table extends Task, ACL rules on parent tables also apply. This script checks the inheritance chain for the fields that were missing:

// READ ONLY — Parent table ACL check
// No changes made

var tables = ['task', 'your_parent_table', 'your_table_name']; // <-- update
var fields  = ['number', 'work_notes', 'comments_and_work_notes'];

for (var t = 0; t < tables.length; t++) {
    for (var f = 0; f < fields.length; f++) {
        var tbl   = tables[t];
        var field = fields[f];

        var gr = new GlideRecord('sys_security_acl');
        gr.addQuery('name', tbl + '.' + field);
        gr.addQuery('active', true);
        gr.query();

        var found = false;
        while (gr.next()) {
            found = true;
            gs.info(
                '[FOUND] '            + tbl + '.' + field              +
                ' | Op: '             + gr.getValue('operation')        +
                ' | Admin overrides: '+ gr.getValue('admin_overrides')  +
                ' | Roles: '          + (gr.getValue('roles') || 'NONE')+
                ' | sys_id: '         + gr.getValue('sys_id')
            );
        }

        if (!found) {
            gs.info('[MISSING] No active ACL: ' + tbl + '.' + field);
        }
    }
}

gs.info('========== Done — no changes made ==========');

Step 3 — Understand what the output reveals

The scripts revealed this pattern:

[ACL] your_table | Op: read | Admin overrides: 1 | Roles: NONE

[MISSING] No active ACL: your_table.number
[MISSING] No active ACL: your_table.work_notes
[FOUND]   task.work_notes | Op: read | Admin overrides: 1 | Roles: NONE

To understand why this causes the problem, two ServiceNow ACL concepts are important.

What admin_overrides = true means

Per ServiceNow KB0685046: when Admin overrides is checked on an ACL, users with the admin role automatically pass that ACL's permissions check — regardless of any role, condition, or script defined on it. Non-admin users must still satisfy all requirements normally.

Two exceptions worth knowing:

  • The nobody role takes precedence — an ACL assigned the nobody role blocks even admins, regardless of the admin_overrides setting.
  • The system property glide.security.admin.override.accessterm controls this behaviour. Its default is true. If it has been changed to false in your instance, admin_overrides may not work as expected.

How ServiceNow finds a matching field ACL

Per ServiceNow product documentation, ACL evaluation uses two gates. A user must pass Gate 1 before Gate 2 is checked. Within each gate, ServiceNow searches from most specific to most generic. The first ACL that evaluates to GRANT stops the search. If no ACL grants access, the field is denied.

Note on Xanadu and later releases: The Xanadu release introduced a new ACL decision type called Deny-Unless. Deny-Unless ACLs are always evaluated before Allow-If ACLs. If a user fails a Deny-Unless check, access is denied immediately. All existing ACLs prior to Xanadu were automatically converted to Allow-If type. The scenario described in this article involves standard Allow-If ACLs — the OOB type you will see on most instances.

Gate 1 — Table-level read ACL:
  Checks in order: your_table → task (parent) → * (wildcard)
  Non-admins pass because the table read ACL has no role restriction.
  Admins pass automatically via admin_overrides.
  Both proceed to Gate 2.

Gate 2 — Field-level read ACL for 'number':
  1. your_table.number   <-- MISSING, continue
  2. task.number         <-- only WRITE ACLs exist here, no read, continue
  3. *.number            <-- nothing, continue
  4. your_table.*        <-- nothing, continue
  5. task.*              <-- nothing, continue
  6. *.*                 <-- nothing, continue

  Result: no ACL granted access → field is DENIED for non-admins

The same Gate 2 search happens for work_notes. Even though task.work_notes read ACL exists, whether non-admins pass it depends on what roles it requires in your specific instance. Always check this using Script 2 above.

Step 4 — The fix

Create field-level read ACLs for number and work_notes directly on your table. This gives Gate 2 a matching ACL to GRANT access at step 1 of the search, before reaching any restrictive parent ACL.

Before you start: Creating ACLs requires the security_admin role. Elevate it first: profile picture dropdown → Elevate Roles → check security_admin.

Navigate to: System Security → Access Control (ACL) → New

Create ACL 1 — Number field read:

Type            : record
Operation       : read
Name — Table    : your_table_name
Name — Field    : number
Active          : checked
Admin overrides : checked
Requires role   : your_role_name
Description     : Allow [role] to read the number field on [table_name]

Create ACL 2 — Work Notes field read:

Type            : record
Operation       : read
Name — Table    : your_table_name
Name — Field    : work_notes
Active          : checked
Admin overrides : checked
Requires role   : your_role_name
Description     : Allow [role] to read the work_notes field on [table_name]

Role guidance: Use the role your non-admin users already have. Match the role pattern used by other field-level ACLs on the same table. Do not leave it blank unless you want all authenticated users to be able to read the field.


Result

After creating both field-level read ACLs, non-admin users with the correct role could immediately see both fields. No cache clear or re-login was needed.

Field       | Root cause                                    | Why fix works
------------|-----------------------------------------------|----------------------------
number      | No field-level read ACL in the entire chain   | New ACL matches at step 1
            | → Gate 2 found nothing → field DENIED         | of Gate 2 → GRANT → done 
------------|-----------------------------------------------|----------------------------
work_notes  | No field-level read ACL on your table →       | New ACL matches at step 1
            | fell back to task.work_notes which may         | of Gate 2 → GRANT → done 
            | require a role non-admins do not have          |

Key Takeaways

  1. Admin overrides = true means the admin role automatically passes that ACL — per KB0685046, this permits admins to pass the permissions check regardless of roles, conditions, or scripts on the ACL. Non-admins must satisfy all requirements normally. Exception: the nobody role overrides this completely. Also be aware of the system property glide.security.admin.override.accessterm — if it has been changed from its default of true, admin_overrides behaviour will differ from what most developers expect.
  2. When no field-level ACL grants access, the field is denied — ServiceNow searches the full inheritance chain from most specific to most generic. If nothing grants access, the field is denied. No error appears — the field simply disappears or shows blank. The only ways to find out which ACL is responsible are Debug Security Rules or a diagnostic background script.
  3. Work Notes needs both a read ACL and a write ACL — work_notes entries are stored in the sys_journal_field table, not directly in your table. The read ACL controls whether past entries appear in the activity stream. The write ACL controls whether the input box is shown. Both are needed. A missing read ACL hides past entries. A missing write ACL hides the input box.
  4. Parent table ACLs apply to all extending child tables — ACL rules on the Task table automatically apply to every table that extends it. The source of a field visibility problem may not be on your table at all. Always check parent table ACLs using Script 2.
  5. Never test ACL changes as admin — always impersonate — admins automatically pass ACLs that have admin_overrides checked, giving a false positive. Impersonate a non-admin user with the relevant role. Then enable Debug Security Rules (System Security → Debugging → Debug Security Rules) and open the record — a debug icon appears next to each field showing exactly which ACL passed or failed.

Diagnostic Checklist

When a field is missing for non-admin users on a table extending Task:

[ ] Elevate to security_admin before working with ACLs
    Profile dropdown → Elevate Roles → check security_admin

[ ] Confirm the field is on the form layout
    Right-click form header → Configure → Form Layout

[ ] Check for a field-level READ ACL on your_table.fieldname
    System Security → Access Control → filter Name = your_table.fieldname, Op = read

[ ] Check Script 2 output for parent table field ACLs
    Look at what roles task.fieldname read requires in your instance

[ ] Confirm the affected user has the role the ACL requires
    User record → Roles tab

[ ] Verify system property glide.security.admin.override.accessterm = true
    System Properties → search the property name
    Default is true — if false, admin_overrides may behave unexpectedly

[ ] Check for a UI Policy hiding the field
    System UI → UI Policies → filter by your table

[ ] Check for a Data Policy affecting the field
    System Policy → Data Policies → filter by your table

[ ] Check Dictionary for read_only = true
    System Definition → Dictionary → search your table + field name
    If true → create a Dictionary Override on your table, set read_only = false

[ ] Test by impersonating a non-admin user with the correct role
    Profile picture → Impersonate User

[ ] Enable Debug Security Rules while impersonating
    System Security → Debugging → Debug Security Rules
    Open the record — click the debug icon next to the field
    to see exactly which ACL passed or failed

Related community posts

These posts address parts of this issue :


If this helped, please mark it as Helpful. If you ran into a variation not covered here, drop a comment — happy to help.

Selva Arun ~ Meena

Version history
Last update:
an hour ago
Updated by:
Contributors