Foster Hardie
Tera Expert

Symptom

You can see a record in a list, but when you click on it, it does not exist. You cannot delete it, even from code.

If you look closer, you notice that the URL when you try to open it is for a ".do" page that is different from the list you were looking at. For instance, you start with a list of tasks (task_list.do) but when you click and the record is not found the URL is incident.do; likewise cmdb_ci_list.do might become cmdb_ci_server.do.

Theory

The record is from an extended table and has been partially deleted or otherwise corrupted.

Under the covers, when a table is extended (e.g., incident extends task) there are really 2 tables: task and incident. The task table contains all the task columns, and the incident contains the columns added to extend task into an incident. They share the same sys_id, and when an incident is loaded into a form both records are read and all columns presented according to the form view.

When viewing records on the task list, you see all tasks, including those that also have incident columns added in the extended incident table. When you click to open the record, ServiceNow looks at the sys_class_name column in the root task table. If the class is simply "task" it knows it does not have any extension and loads the record into a task form view. However, if the class is "incident" it knows it needs to select from the incident table, then add the task columns and load it all into an incident form view. So what happens if the class is incident, but somehow the incident table does not have a record corresponding to the task record?

I suspect the order of operations, even from code, is to evaluate class, then begin the operation on the furthest extension table (extension can be multiple levels deep). So even code fails to locate a record to delete because, in the task example, it would find the record in the query of GlideRecord('task') but then fail to locate the incident in the subsequent task.deleteRecord() because the delete order of operation would start at the class/extended record and work it's way down (cascade) to the root record type.

Solution

There may be others, but this is what I came up with.

  1. From the list view, filter until you see the single record that you are unable to open/delete.
  2. Export XML (hamburger menu on any column header)
  3. Edit the exported file:
    1. Search for sys_class_name, change the value from the extended table to the root table (e.g., from "incident" to "task")
    2. Search for 2 other occurrences of the extended table name: one near the beginning of the XML document, and one near the end. E.g., search for "<incident" and "</incident" and change them to "<task" and "</task".
    3. Save
  4. Import the record back into the list (hamburger menu on any column header: Import XML)
  5. This will overwrite the record by matching the sys_id, but with the class name matching the root table instead of the extended table. Now you can open it (e.g., in the task.do form) and, if you wish, delete it.
Comments
Maik Skoddow
Tera Patron
Tera Patron

Interesting!

Foster Hardie
Tera Expert

I seem to have accidentally discovered another way to delete these records. I can't promise this will always work, but it worked in this case.

I had 90k+ sys_users that were loading as customer_contacts. Rather than export all that XML, I just wrote an update script to update the sys_class_name to sys_user from customer_contact. Whammo, they all disappeared! Not sure why this resulted in a delete, but I'll take it.

Version history
Last update:
‎04-13-2022 07:26 AM
Updated by: