Skip to main content

ServiceNow Error when copying flow: "Label cache error: Table not found"

ServiceNow Error when copying flow

Label cache error: Table not found: x_cls_clear_skye_i_identity_settings: Label cache error: Table not found: x_cls_clear_skye_i_identity_settingswhen copying the promote profile subflowreproduceable on 5.2 and 3

KB1575415 - How To Remove Unnecessary Label Cache Entries of a Flow/Subflow/Action

11:43

Apparently, running the background script detailed in the SN KB should resolve the issue

once happy, modify the fix script below to set debugmode to false

---

Fix script:

// README

// This script updates and removes unnecessary label cache entries of a flow/subflow (defintion and master snapshot).

//

// IMPORTANT NOTES: 

// This script only deletes the unnecessary label cache in the server. UI can hold a copy of a label cache.

// Therefore refresh the browser that has the action opened once this script is executed for the action.

// If this script is executed and the action is saved using UI, the previous label cache (before the delete)

// will be saved since the label cache from the UI will be sent to the server and saved.

// Script Parameters

// If true then do not actually modify the flow/subflow's label cache (debug mode). If false then update the flow/subflow's label cache (real mode).

var debugMode = true;

// If empty, then execute for all flow/subflow definition and master snapshot files. Otherwise, only run for the flow/subflow/action with name = targetName.

var targetName = "";

// If true then it logs more debug messages.

var skipDetails = false;

// Code

var missCount = 0;

var hitCount = 0;

var numberOfRecordsModified = 0;

var numberOfFlowsModified = 0;

var flowsModified = {};

function cleanFlowLabelCache(flow, debug, flowVersion) {

    var flowModified = false;

    printWithName(flow.name, "Attempting to clean the label cache for flow/subflow \'" + flow.name + "\' with sys id \'" + flow.sys_id + "\' which is on the table " + flow.sys_class_name);

    if (isEmpty(flow.label_cache) || flow.label_cache == "[]") {

        printWithName(flow.name, "Has an empty label cache");

    } else {

        var pills = getPills(flow.sys_id, flowVersion);

        // removes control characters in unicode (ex: \n), which the inclusion will cause a thrown exception in JSON.parse()

        flow.label_cache = flow.label_cache.toString().replace(/[\u0000-\u001F\u007F-\u009F]/g, "");

        var label_cache = {};

        try {

            label_cache = JSON.parse(flow.label_cache);

        } catch(e) {

            gs.print("Exception thrown. Message: " + e.message + " Stack trace: " + e.stack);

        }

        printWithName(flow.name, "Original label cache before the removal:\n" + flow.name + ": " + flow.label_cache);

        var newLabelCache = [];

        for (var labelKey in label_cache) {

            if (pills[label_cache[labelKey].name] || pills['{{' + label_cache[labelKey].name + '}}'] ) {

                if (skipDetails == false)

                    printWithName(flow.name, "We have a match " + label_cache[labelKey].name);

                newLabelCache.push(label_cache[labelKey]);

                hitCount++;

            } else {

                if (skipDetails == false)

                    printWithName(flow.name, "We have a miss " + label_cache[labelKey].name);

                missCount++;

                flowModified = true;

            }

        }

        flow.label_cache = JSON.stringify(newLabelCache);

        printWithName(flow.name, "New label cache after the removal:\n" + flow.name + ": " + flow.label_cache);

        if (debug == false)

            flow.update();

    }

    return flowModified;

}

function isEmpty(obj) {

    return !obj || (Object.keys(obj).length === 0 && obj.constructor === Object)

}

function getPills(flowId, flowVersion) {

    var pills = {};

    var instances = new GlideRecord("sys_hub_flow_component");

    instances.addQuery("flow", flowId);

    instances.query();

    while(instances.next()) {

        if (flowVersion == "2") {

            findPillsInInstanceCompressedString(instances, pills);

        } else {

            findPillsInMapping(instances.sys_id, pills);

            findPillsInVariableValue(instances.sys_id, pills);

        }

    }

    findPillsInMapping(flowId, pills);

    findPillsInVariableValue(flowId, pills);

    if (flowVersion == "2") {

        findPillsInTriggerInstanceV2(flowId, pills);

    } else {

        findPillsInTriggerInstance(flowId, pills);

    }

    return pills;

}

function getUncompressedValue(compressedValues) {

    if (skipDetails == false)

        gs.print("compressedValues: " + compressedValues);

    var values = "";

    if (compressedValues)

        values = new GlideCompressionUtil().expandToString(new GlideStringUtil().base64DecodeAsBytes(compressedValues));

    if (skipDetails == false)

        gs.print(values);

    return values;

}

function findPillsInInstanceCompressedString(instances, pills) {

    var tableName = null;

    if (instances.sys_class_name == "sys_hub_action_instance_v2") {

        tableName = "sys_hub_action_instance_v2";

    } else if (instances.sys_class_name == "sys_hub_sub_flow_instance_v2") {

        tableName = "sys_hub_sub_flow_instance_v2";

    } else if (instances.sys_class_name == "sys_hub_flow_logic_instance_v2") {

        tableName = "sys_hub_flow_logic_instance_v2";

    }

    if (tableName) {

        var instanceGr = new GlideRecord(tableName);

        if (instanceGr.get(instances.sys_id)) {

            var compressedValues = null;

            if (tableName == "sys_hub_sub_flow_instance_v2")

                compressedValues = instanceGr.subflow_inputs + '';

            else if (tableName == "sys_hub_action_instance_v2" || tableName == "sys_hub_flow_logic_instance_v2") {

                compressedValues = instanceGr.values + '';

            }

            if (compressedValues) {

                var values = getUncompressedValue(compressedValues);

                findPillsInValue(values, pills);

            }

        }

    }

}

function findPillsInTriggerInstanceV2(flowId, pills) {

    var triggerInstance = new GlideRecord("sys_hub_trigger_instance_v2");

    triggerInstance.addQuery("flow", flowId);

    triggerInstance.query();

    // should be at max one result in triggerInstance

    while(triggerInstance.next()) {

        var compressedValues = triggerInstance.trigger_inputs + '';

        var values = getUncompressedValue(compressedValues);

        findPillsInValue(values, pills);

    }

}

function findPillsInTriggerInstance(flowId, pills) {

    var triggerInstance = new GlideRecord("sys_hub_trigger_instance");

    triggerInstance.addQuery("flow", flowId);

    triggerInstance.query();

    // should be at max one result in triggerInstance

    while(triggerInstance.next()) {

        var triggerInstanceId = triggerInstance.sys_id;

        var sysVariableValue = new GlideRecord("sys_variable_value");

        sysVariableValue.addQuery("document_key", triggerInstanceId);

        sysVariableValue.query();

        while(sysVariableValue.next()) {

            var value = sysVariableValue.value;

            findPillsInValue(value, pills);

        }

    }

}

function findPillsInMapping(id, pills) {

    var elementMapping = new GlideRecord("sys_element_mapping");

    elementMapping.addQuery("id", id);

    elementMapping.query();

    while(elementMapping.next()) {

        var value = elementMapping.value;

        findPillsInValue(value, pills);

    }

}

function findPillsInVariableValue(id, pills) {

    var variableValue = new GlideRecord("sys_variable_value");

    variableValue.addQuery("document_key", id);

    variableValue.query();

    while(variableValue.next()) {

        var value = variableValue.value;

        findPillsInValue(value, pills);

    }

};

function findPillsInTransform (value, pills) {

    // removes "{{fd_transform:" prefix and "}}" postfix

    if (value && value.length > 16) {

        var uuid = value.substring(15, value.length - 2);

        var pillCompound = new GlideRecord("sys_hub_pill_compound");

        pillCompound.addQuery("ui_id", uuid);

        pillCompound.query();

        while (pillCompound.next()) {

            var prescription = pillCompound.prescription;

            if (skipDetails == false)

                gs.print("Prescription value: " + prescription);

            findPillsInValue(prescription, pills);

        }

    }

};

function findPillsInValue (value, pills) {

    var result;

    var PILL_REGEX = /{{(?!{)([^}]*)}}/g;

    while(result = PILL_REGEX.exec(value)) {

        if (result[0] && result[0].startsWith("{{fd_transform:") && result[0].length > 16) {

            findPillsInTransform(result[0], pills);

        } else {

            pills[result[0]] = true;

        }

        if (skipDetails == false)

            gs.print("Pill value: " + result[0]);

    }

};

function findPillsInErrorHandling(id, pills) {

    var errorHandlingMetadata = new GlideRecord("sys_hub_action_status_metadata");

    if (!errorHandlingMetadata.get("action_type_id", id))

        return;

    var statusCondition = new GlideRecord("sys_hub_status_condition");

    statusCondition.addQuery("action_status_metadata_id", errorHandlingMetadata.sys_id);

    statusCondition.query();

    while (statusCondition.next()) {

        findPillsInValue(statusCondition.status, pills);

        findPillsInValue(statusCondition.condition, pills);

    }

}

function printWithName(name, text) {

    gs.print(name + ": " + text);

}

var modified;

gs.print("Debug mode: " + debugMode);

if (debugMode == false)

    gs.print("This is an actual execution that updates the label cache");

else

    gs.print("This is a test run that does not actually update the label cache");

var flows = new GlideRecord("sys_hub_flow_snapshot");

flows.addQuery("master", true);

if (targetName)

    flows.addQuery("name", targetName);

flows.query();

while (flows.next()) {

    var version = flows.getValue("version");

    var flowVersion = "1";

    if (version == "2") {

        flowVersion = "2";

    }

    modified = cleanFlowLabelCache(flows, debugMode, flowVersion);

    if (modified) {

        numberOfRecordsModified++;

        if (!flowsModified[flows.name]) {

            flowsModified[flows.name] = true;

            numberOfFlowsModified++;

        }

    }

}

flows = new GlideRecord("sys_hub_flow");

if (targetName)

    flows.addQuery("name", targetName);

flows.query();

while (flows.next()) {

    var version = flows.getValue("version");

    var flowVersion = "1";

    if (version == "2") {

        flowVersion = "2";

    }

    modified = cleanFlowLabelCache(flows, debugMode, flowVersion);

    if (modified) {

        numberOfRecordsModified++;

        if (!flowsModified[flows.name]) {

            flowsModified[flows.name] = true;

            numberOfFlowsModified++;

        }

    }

}

gs.print("Total number of label cache entries removed: " + missCount);

gs.print("Total number of label cache entries kept (not removed): " + hitCount);

gs.print("Total number of flow/subflow definition and master snapshot records modified: " + numberOfRecordsModified);

gs.print("Total number of unique flow flow/subflow modified: " + numberOfFlowsModified);


Comments

Popular posts from this blog

ServiceNow check for null or nil or empty (or not)

Haven't tested these all recently within global/local scopes, so feel free to have a play! option 1 use an encoded query embedded in the GlideRecord , e.g.  var grProf = new GlideRecord ( 'x_cls_clear_skye_i_profile' ); grProf . addQuery ( 'status=1^ owner=NULL ' ); grProf . query (); even better use the glideRecord  addNotNullQuery or addNullQuery option 2 JSUtil.nil / notNil (this might be the most powerful. See this link ) example: if ( current . operation () == 'insert' && JSUtil . notNil ( current . parent ) && ! current . work_effort . nil ())  option 3 there might be times when you need to get inside the GlideRecord and perform the check there, for example if the code goes down 2 optional routes depending on null / not null can use gs.nil : var grAppr = new GlideRecord ( 'sysapproval_approver' ); var grUser = new GlideRecord ( 'sys_user' ); if ( grUser . get ( 'sys_id' , current . approver )){...