Troubleshooting “Insufficient stack to continue executing the program safely”
13 min
error an activate script fails with the following exception exception insufficient stack to continue executing the program safely this can happen from having too many functions on the call stack or function on the stack using too much stack space what this means activate detected that a script was close to exhausting the available call stack and stopped it safely this protection prevents uncontrolled script recursion from causing a stackoverflowexception, which could terminate the process running the script, such as the activate orchestrator the protection is working as intended restarting the service or resubmitting the job may clear the immediate failure, but it will not correct the script that caused it most likely cause the usual cause is a script method that repeatedly calls itself without reaching a valid exit condition recursion may be direct a method calls itself indirect method a calls method b, which calls method a data dependent recursion stops for most inputs but not for a particular record hook driven a hook calls an operation that triggers the same hook again cross script scripts invoked through evaluator invoke, included helpers, or another activate operation form a call cycle excessively deep the recursion is valid, but the input creates more nested calls than the stack can safely support a function using unusually large amounts of stack space can also produce the error, although this is less common in activate scripts example of direct recursion the following method has no terminating condition public object processitem(string item) { return processitem(item); } every call adds another method to the call stack activate eventually stops the script and reports the insufficient stack exception how to troubleshoot the error 1\ identify the failing script use the failed job details and exception stack trace to identify the job and workflow step that failed the script parameter or web form serverscript being executed the last script method shown in the stack trace any hook, shared helper, //@include, or imported script involved the input object or arguments being processed when the error occurred repeated method names in the stack trace are a strong indication of recursion 2\ trace the complete call path starting from the failing method, follow every method and activate operation it calls look for cycles such as processuser > updateuser > runupdatehook > processuser the cycle may cross script boundaries, so inspect calls made through evaluator invoke( ) evaluator invokeoptional( ) job executescript( ) included or imported script helpers child jobs or workflows connector and request hooks operations that can trigger the same event again 3\ check the exit condition for each recursive method, confirm that a clear terminating condition exists every recursive call makes measurable progress toward that condition the terminating value can actually be reached null, empty, cyclic, or unexpected input cannot bypass the condition the recursion depth has a sensible maximum for hierarchical data, also check for cycles a parent child relationship such as a > b > c > a will recurse indefinitely unless previously visited objects are tracked 4\ check hook re entry a hook can cause indirect recursion when it performs the same operation that originally triggered the hook for example update operation > onupdate hook > performs another update > onupdate hook > performs another update where possible, change the hook so it modifies the current request rather than starting the same operation again if re entry is necessary, add an explicit guard using a job argument or other request scoped marker public object runhook() { var guard = job arguments\["hookalreadyrunning"]; if (guard != null && guard tostring() == "true") { return null; } job arguments\["hookalreadyrunning"] = "true"; try { runprotectedoperation(); } finally { job arguments\["hookalreadyrunning"] = "false"; } return null; } use a guard that is scoped to the current job or operation avoid a global or static flag because concurrent jobs could interfere with one another 5\ replace recursion where practical for lists, trees, and parent child structures, prefer an explicit loop, queue, or stack this avoids consuming one program stack frame for every item when traversing data that may contain cycles, track visited identifiers public object processhierarchy(item root) { var pending = new stack\<item>(); var visited = new hashset\<string>(); pending push(root); while (pending count > 0) { var item = pending pop(); if (!visited add(item id)) { continue; } processitem(item); foreach (var child in item children) { pending push(child); } } return null; } 6\ test with the input that caused the failure after correcting the script test it with the original failing input test empty and null inputs test cyclic or self referencing data where applicable test an unusually deep hierarchy confirm the job completes without repeatedly invoking the same hook or method what not to do do not disable or attempt to bypass the stack protection catch the exception and retry the same operation increase recursion limits without first finding the call cycle repeatedly resubmit the failed job without changing the script or data assume a service restart has resolved the underlying problem catching and ignoring the exception can leave the job partially processed and does not make further script execution safe when to contact activate support contact activate support if the stack trace contains only activate product methods and does not identify a customer authored script the problem occurs after an upgrade with no relevant script changes the script has a bounded call depth but still fails with shallow input you cannot identify which hook or script is re entering the failure occurs in a standard, unmodified activate package script include the following information activate version and build number failed job id full exception and stack trace name and path of the affected script parameter the workflow step or hook being executed relevant job arguments, with credentials and sensitive data removed whether the script is standard, customised, imported, or included by another script the smallest input that reproduces the problem