- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
01-28-2023 08:41 PM - edited 12-25-2023 10:09 PM
ServiceNow is built upon a database consisting of tables, which are collections of records. Each record corresponds to a row in a table, and each field on a record corresponds to a column on that table. Users can enter data in fields in many different ways. What and how users can enter data depends on the field type. And one of these field types is called "Reference" which represents a pointer to a record in another table. That pointer is nothing else than the well-known "Sys ID" which is unique for a certain record.
|
|
What are broken references?
Basically ServiceNow will remove all references to a record which is being deleted, but there are many situations where this may not work. As a result, the referencing records still have Sys ID values in their reference fields, but they now point to a non-existing target record. Unfortunately, ServiceNow does not mark such broken references in a special way, and it seems that fields with broken references are just empty.
But the knowledge about broken references is important because they can be an indicator of malfunctioning functionalities or invalid import data. They also can point you into the right direction in case of any "strange" behavior.
How to identify broken references in the list view
If you just want to check the values for a certain column in a certain table, you can make use of a little filter query trick. The pattern for such a query is as follows:
[FIELD NAME] is not empty AND [FIELD NAME].sys_id is empty
In my PDI I prepared a change request record with a broken reference at field "Requested by". The applied filter returns that record but the value is displayed as "(empty)":
How to identify broken references in the form view
On the level of a single record it is easier to identify all reference fields with broken references: With the help of the Dictionary you just have to iterate over all reference fields with a value and if the referenced record cannot be loaded (provided that the logged-in user is allowed to read the referenced record), it is broken.
For the evaluation and printing the respective error message, a "display" Business Rule would be the perfect solution candidate. Unfortunately, you cannot define "global" Business Rules which are fired for any table. But Client Scripts on the fictive "global" table have that capability, as they are fired for every form load. And with the help of a GlideAjax call to a prepared client-callable Script Include the fields with broken references can be identified.
For such an approach you basically would leverage a "onLoad" Client Script, but during the implementation I came across an issue. The code inside the "onLoad()" function is not necessarily executed after the form is fully loaded and all client-side code/functions are executed. But to print the invisible value below a reference field with a broken reference, you have to wait until everything is loaded and all client-side codes have been performed. Only that way you can be sure that a field is still visible on the form. After searching around a while, I found the undocumented function addLateLoadEvent() which adds a function without parameters to a list of function callbacks to run after ServiceNow has sufficiently loaded the client side codes and forms.
Script Include
The following Script Include can be called from client-side as well as on server-side. It requires two parameters: the table name and the Sys ID of the respective record to be examined.
At the end, it will return an array of objects - one for each field with a broken reference. These objects contain all required properties of the identified fields: the internal technical name, the field label and the value.
In case the call was performed from client-side the returned array is serialized to a string representation to make sure it can be transferred to the client.
var BrokenReferenceUtils = Class.create();
BrokenReferenceUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {
getFieldsWithBrokenReferences : function(strTableName, strSysId) {
var _arrFields = [];
var _isAjaxCall = false;
var _strTableName = strTableName || '';
var _strSysId = strSysId || '';
if (this.getParameter('sysparm_table_name') && this.getParameter('sysparm_sys_id')) {
_strTableName = String(this.getParameter('sysparm_table_name')).trim();
_strSysId = String(this.getParameter('sysparm_sys_id')).trim();
_isAjaxCall = true;
}
if (_strTableName.length > 3 && gs.tableExists(_strTableName) && _strSysId.length === 32) {
var _grRecord = new GlideRecord(_strTableName);
if (_grRecord.get(_strSysId)) {
var _arrTableHierarchy = j2js(new TableUtils(_grRecord.getTableName()).getTables());
var _grReferenceFields = new GlideRecord('sys_dictionary');
_grReferenceFields.addEncodedQuery(
'internal_type=reference^' +
'active=true^' +
'nameIN' + _arrTableHierarchy.join(',')
);
_grReferenceFields.query();
while (_grReferenceFields.next()) {
var _strFieldName = _grReferenceFields.getValue('element');
var _strFieldLabel = _grReferenceFields.getValue('column_label');
var _strTargetTable = _grReferenceFields.getValue('reference');
var _strFieldValue = _grRecord.getValue(_strFieldName);
if (JSUtil.notNil(_strFieldValue) && JSUtil.notNil(_strTargetTable)) {
var _grTargetRecord = new GlideRecord(_strTargetTable);
if (!_grTargetRecord.get(_strFieldValue)) {
_arrFields.push({
"field_name" : _strFieldName,
"field_label": _strFieldLabel,
"field_value": _strFieldValue
})
}
}
}
}
}
return _isAjaxCall ? JSON.stringify(_arrFields) : _arrFields;
},
type: 'BrokenReferenceUtils'
});
Client Script
The following Client Script performs an Ajax call to the above Script Include and prints the warning in case fields with broken references have been identified.
Table | Global [global] |
Global | true |
UI Type | Desktop |
Type | onLoad |
Script |
|
Further information
How to find records with a Broken Reference from a List Filter
Knowledge article on the support portal.
Unique record identifier (sys_id)
Documentation page about ServiceNow's concept of Sys IDs.
Universal Pattern for Script Includes
This article presents a generally applicable implementation pattern for Script Includes that covers all invocation variants and also has a standard structure to support clean and maintainable code.
- 3,477 Views

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
This is amazing and much useful when the reference record is not found. Thank you @Maik Skoddow 🙂