# Automation Professionals' Integration Toolkit Module

**URL:** <https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934>\
**Category:** 3rd Party Modules\
**Tags:** perspective, scripting, ignition83, vision, ignition81\
**Created:** [June 13, 2023, 12:42am UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934 "2023-06-13T00:42:35Z")\
**Posts on this page:** 20\
**Page:** 17

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [February 3, 2026, 7:10pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/322 "2026-02-03T19:10:51Z")

</div>

> [@pturmel](#):
>
> debug level for ToolkitRPCImpl

Here you go:

```python
ToolkitRPCImpl

03Feb2026 14:09:46
Unexpected error delegating to toolkitTest.gwLogInfof
java.util.NoSuchElementException: No value present

at java.base/java.util.Optional.orElseThrow(Unknown Source)

at com.automation_pros.simaids.gateway.ToolkitRPCImpl.runInGateway(ToolkitRPCImpl.java:77)

at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

at java.base/java.lang.reflect.Method.invoke(Unknown Source)

at com.inductiveautomation.ignition.gateway.rpc.RpcDelegate$DelegateRpcHandler.handle(RpcDelegate.java:275)

at com.inductiveautomation.ignition.gateway.rpc.RpcRoutes.lambda$handle$1(RpcRoutes.java:209)

at com.inductiveautomation.ignition.gateway.rpc.RpcRoutes.safelyHandle(RpcRoutes.java:225)

at com.inductiveautomation.ignition.gateway.rpc.RpcRoutes.handle(RpcRoutes.java:209)

at com.inductiveautomation.ignition.gateway.dataroutes.Route.service(Route.java:355)

at com.inductiveautomation.ignition.gateway.dataroutes.RouteGroupImpl.service(RouteGroupImpl.java:117)

at com.inductiveautomation.ignition.gateway.dataroutes.RouteGroupCollectionServlet.serviceInternal(RouteGroupCollectionServlet.java:152)

at com.inductiveautomation.ignition.gateway.dataroutes.AbstractRouteGroupServlet.service(AbstractRouteGroupServlet.java:40)

at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614)

at org.eclipse.jetty.ee10.servlet.ServletHolder$NotAsync.service(ServletHolder.java:1385)

at org.eclipse.jetty.ee10.servlet.ServletHolder.handle(ServletHolder.java:751)

at org.eclipse.jetty.ee10.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1622)

at com.inductiveautomation.catapult.filters.GatewayFilter.doFilter(GatewayFilter.java:119)

at jakarta.servlet.http.HttpFilter.doFilter(HttpFilter.java:97)

at org.eclipse.jetty.ee10.servlet.FilterHolder.doFilter(FilterHolder.java:205)

at org.eclipse.jetty.ee10.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594)

at org.eclipse.jetty.ee10.servlet.ServletHandler$MappedServlet.handle(ServletHandler.java:1555)

at org.eclipse.jetty.ee10.servlet.ServletChannel.dispatch(ServletChannel.java:823)

at org.eclipse.jetty.ee10.servlet.ServletChannel.handle(ServletChannel.java:440)

at org.eclipse.jetty.ee10.servlet.ServletHandler.handle(ServletHandler.java:470)

at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:575)

at org.eclipse.jetty.ee10.servlet.SessionHandler.handle(SessionHandler.java:717)

at org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:1071)

at org.eclipse.jetty.rewrite.handler.RewriteHandler$LastRuleHandler.handle(RewriteHandler.java:159)

at org.eclipse.jetty.rewrite.handler.Rule$Handler.handle(Rule.java:108)

at org.eclipse.jetty.rewrite.handler.HeaderPatternRule$1.handle(HeaderPatternRule.java:89)

at org.eclipse.jetty.rewrite.handler.Rule$Handler.handle(Rule.java:108)

at org.eclipse.jetty.rewrite.handler.HeaderPatternRule$1.handle(HeaderPatternRule.java:89)

at org.eclipse.jetty.rewrite.handler.Rule$Handler.handle(Rule.java:108)

at org.eclipse.jetty.rewrite.handler.HeaderPatternRule$1.handle(HeaderPatternRule.java:89)

at org.eclipse.jetty.rewrite.handler.Rule$Handler.handle(Rule.java:108)

at org.eclipse.jetty.rewrite.handler.HeaderPatternRule$1.handle(HeaderPatternRule.java:89)

at org.eclipse.jetty.rewrite.handler.Rule$Handler.handle(Rule.java:108)

at org.eclipse.jetty.rewrite.handler.RewriteHandler.handle(RewriteHandler.java:143)

at org.eclipse.jetty.server.Handler$Sequence.handle(Handler.java:805)

at org.eclipse.jetty.server.Handler$Sequence.handle(Handler.java:805)

at org.eclipse.jetty.server.Server.handle(Server.java:182)

at org.eclipse.jetty.server.internal.HttpChannelState$HandlerInvoker.run(HttpChannelState.java:677)

at org.eclipse.jetty.server.internal.HttpConnection.onFillable(HttpConnection.java:416)

at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:322)

at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105)

at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)

at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:480)

at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:443)

at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:293)

at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.run(AdaptiveExecutionStrategy.java:201)

at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:311)

at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:981)

at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1211)

at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1166)

at java.base/java.lang.Thread.run(Unknown Source)

```

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [February 3, 2026, 7:22pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/323 "2026-02-03T19:22:47Z")

</div>

Huh. Not helpful. And seems I never properly published v2.1.2... Coming up.

For Ignition v8.3+: [v2.1.2.253151852](https://www.automation-pros.com/ignition/Integration%20Toolkit-2.1.2.253151852-v83.modl)

I found this in December, and queued it up, but didn't push. ):

(v2.1.1 doesn't have the `ClientReqSession` material at all.)

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [February 3, 2026, 7:37pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/324 "2026-02-03T19:37:56Z")

</div>

> [@pturmel](#):
>
> v2.1.2

My example works with `v2.1.2` 👍  
Thanks!

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [February 3, 2026, 7:42pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/325 "2026-02-03T19:42:36Z")

</div>

Is there room for improvement with how lists are coerced back into PyObjects?

```python
# Script Module (toolkitTest)
@system.util.runInGateway
def testReturnList():
	myList = []
	return myList

# Script Console
toolkitTest.testReturnList()

```

```python
Java Traceback:
Traceback (most recent call last):
  File "<input>", line 3, in <module>
java.lang.ClassCastException: class java.util.Collections$UnmodifiableRandomAccessList cannot be cast to class org.python.core.PyObject (java.util.Collections$UnmodifiableRandomAccessList is in module java.base of loader 'bootstrap'; org.python.core.PyObject is in unnamed module of loader java.net.URLClassLoader @7c2bfbb3)

	at jdk.proxy2/jdk.proxy2.$Proxy86.runInGateway(Unknown Source)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction. __gateway_call__ (RunInGateway.java:51)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction. __call__ (RunInGateway.java:41)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction. __call__ (RunInGateway.java:56)

	at org.python.pycode._pyx65.f$0(<input>:3)

	at org.python.pycode._pyx65.call_function(<input>)

	at org.python.core.PyTableCode.call(PyTableCode.java:173)

	at org.python.core.PyCode.call(PyCode.java:18)

	at org.python.core.Py.runCode(Py.java:1703)

	at org.python.core.Py.exec(Py.java:1747)

	at org.python.util.PythonInterpreter.exec(PythonInterpreter.java:277)

	at org.python.util.InteractiveInterpreter.runcode(InteractiveInterpreter.java:130)

	at com.inductiveautomation.ignition.designer.gui.tools.jythonconsole.JythonConsole$ConsoleWorker.doInBackground(JythonConsole.java:639)

	at com.inductiveautomation.ignition.designer.gui.tools.jythonconsole.JythonConsole$ConsoleWorker.doInBackground(JythonConsole.java:627)

	at java.desktop/javax.swing.SwingWorker$1.call(Unknown Source)

	at java.base/java.util.concurrent.FutureTask.run(Unknown Source)

	at java.desktop/javax.swing.SwingWorker.run(Unknown Source)

	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)

	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)

	at java.base/java.lang.Thread.run(Unknown Source)

java.lang.ClassCastException: java.lang.ClassCastException: class java.util.Collections$UnmodifiableRandomAccessList cannot be cast to class org.python.core.PyObject (java.util.Collections$UnmodifiableRandomAccessList is in module java.base of loader 'bootstrap'; org.python.core.PyObject is in unnamed module of loader java.net.URLClassLoader @7c2bfbb3)

	at org.python.core.Py.JavaError(Py.java:545)

	at org.python.core.PyTableCode.call(PyTableCode.java:182)

	at org.python.core.PyCode.call(PyCode.java:18)

	at org.python.core.Py.runCode(Py.java:1703)

	at org.python.core.Py.exec(Py.java:1747)

	at org.python.util.PythonInterpreter.exec(PythonInterpreter.java:277)

	at org.python.util.InteractiveInterpreter.runcode(InteractiveInterpreter.java:130)

	at com.inductiveautomation.ignition.designer.gui.tools.jythonconsole.JythonConsole$ConsoleWorker.doInBackground(JythonConsole.java:639)

	at com.inductiveautomation.ignition.designer.gui.tools.jythonconsole.JythonConsole$ConsoleWorker.doInBackground(JythonConsole.java:627)

	at java.desktop/javax.swing.SwingWorker$1.call(Unknown Source)

	at java.base/java.util.concurrent.FutureTask.run(Unknown Source)

	at java.desktop/javax.swing.SwingWorker.run(Unknown Source)

	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)

	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)

	at java.base/java.lang.Thread.run(Unknown Source)

Caused by: java.lang.ClassCastException: class java.util.Collections$UnmodifiableRandomAccessList cannot be cast to class org.python.core.PyObject (java.util.Collections$UnmodifiableRandomAccessList is in module java.base of loader 'bootstrap'; org.python.core.PyObject is in unnamed module of loader java.net.URLClassLoader @7c2bfbb3)

	at jdk.proxy2/jdk.proxy2.$Proxy86.runInGateway(Unknown Source)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction. __gateway_call__ (RunInGateway.java:51)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction. __call__ (RunInGateway.java:41)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction. __call__ (RunInGateway.java:56)

	at org.python.pycode._pyx65.f$0(<input>:3)

	at org.python.pycode._pyx65.call_function(<input>)

	at org.python.core.PyTableCode.call(PyTableCode.java:173)

	... 13 more

Traceback (most recent call last):
  File "<input>", line 3, in <module>
java.lang.ClassCastException: class java.util.Collections$UnmodifiableRandomAccessList cannot be cast to class org.python.core.PyObject (java.util.Collections$UnmodifiableRandomAccessList is in module java.base of loader 'bootstrap'; org.python.core.PyObject is in unnamed module of loader java.net.URLClassLoader @7c2bfbb3)

	at jdk.proxy2/jdk.proxy2.$Proxy86.runInGateway(Unknown Source)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction. __gateway_call__ (RunInGateway.java:51)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction. __call__ (RunInGateway.java:41)

	at com.automation_pros.simaids.client.script.RunInGateway$AsGatewayFunction. __call__ (RunInGateway.java:56)

	at org.python.pycode._pyx65.f$0(<input>:3)

	at org.python.pycode._pyx65.call_function(<input>)

	at org.python.core.PyTableCode.call(PyTableCode.java:173)

	at org.python.core.PyCode.call(PyCode.java:18)

	at org.python.core.Py.runCode(Py.java:1703)

	at org.python.core.Py.exec(Py.java:1747)

	at org.python.util.PythonInterpreter.exec(PythonInterpreter.java:277)

	at org.python.util.InteractiveInterpreter.runcode(InteractiveInterpreter.java:130)

	at com.inductiveautomation.ignition.designer.gui.tools.jythonconsole.JythonConsole$ConsoleWorker.doInBackground(JythonConsole.java:639)

	at com.inductiveautomation.ignition.designer.gui.tools.jythonconsole.JythonConsole$ConsoleWorker.doInBackground(JythonConsole.java:627)

	at java.desktop/javax.swing.SwingWorker$1.call(Unknown Source)

	at java.base/java.util.concurrent.FutureTask.run(Unknown Source)

	at java.desktop/javax.swing.SwingWorker.run(Unknown Source)

	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)

	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)

	at java.base/java.lang.Thread.run(Unknown Source)

java.lang.ClassCastException: java.lang.ClassCastException: class java.util.Collections$UnmodifiableRandomAccessList cannot be cast to class org.python.core.PyObject (java.util.Collections$UnmodifiableRandomAccessList is in module java.base of loader 'bootstrap'; org.python.core.PyObject is in unnamed module of loader java.net.URLClassLoader @7c2bfbb3)

```

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [February 3, 2026, 7:58pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/326 "2026-02-03T19:58:13Z")

</div>

The inevitable pain of (de)serialization is part of the reason I have been so hesitant to do something like this. Looks like Phil's using the base proto based RPC serialization in 8.3, which hands back real (unmodifiable) List instances when given one over RPC, instead of any wrapped Jython thing.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [February 3, 2026, 9:17pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/327 "2026-02-03T21:17:03Z")

</div>

> [@bmusson](#):
>
> room for improvement

Will investigate.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [March 23, 2026, 7:49pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/328 "2026-03-23T19:49:27Z")

</div>

Automation Professionals is pleased to announce new **Production Releases** of this module with the following new [utility functions](https://www.automation-pros.com/toolkit/doc/utility.html#runinclient):

- `system.util.runInClient()`
- `rex()`
- `linesOf()`
- `parseJTime()`
- `formatJTime()`

For Ignition v8.3: ~~v2.1.4.260821836~~

For Ignition v8.1: ~~v2.1.4.260821551~~

I do not have a built-in solution to Ben's list type issue, but I suspect it would be mitigated if the target function for `runInGateway` used my [system.util.unQualify()](https://www.automation-pros.com/toolkit/doc/scripting.html#unqualify) function to re-wrap the return value.

Edit: Also, the expression functions that return lists now return instances of `PyList` instead of `ArrayList`, to make them more friendly to Document tags and Vision Document properties.

---

<div class="post-metadata">

**Author:** ![bschroeder](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bschroeder/32/13260_2.png) [@bschroeder](https://forum.inductiveautomation.com/u/bschroeder)\
**Post date:** [April 1, 2026, 3:24pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/329 "2026-04-01T15:24:46Z")

</div>

I'm trying to walk through a json using a tag path, and then returning the last found key. I can do this easily in a python script, but I'm trying to do it using an expression.

This is how I'm doing it in python:

```python

def getAlarmNode(alarmDoc, path):
    parts = path.split('/')
    node = alarmDoc
    for part in parts:
        node = node.get(part)
        if node is None:
            return None
    return node

```

The alarmDoc is a document tag representing a dictionary of alarms and counts in an area.  
IE

```python
{
	'default':{
		'details':{
			'count':0
		},
		'Process':{
			'details':{
				'count':0
			},
			'Furnace':{
				'details':{
					'count':0
				}
			}
		}
	}
}

```

Basically I'd like to be able to parse a Tag/Folder path that looks like [default]Process or [default]Process/Furnace using an expression rather than a script.

Any pointers as to where to start?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [April 1, 2026, 9:23pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/330 "2026-04-01T21:23:25Z")

</div>

Hmmm. Technically, that's a recursive operation, which I don't support. But I imagine you could hard-code a few levels with `coalesce()`. Start with my [parsePath()](https://www.automation-pros.com/toolkit/doc/datasets.html#parsepath) function.

---

<div class="post-metadata">

**Author:** ![bschroeder](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bschroeder/32/13260_2.png) [@bschroeder](https://forum.inductiveautomation.com/u/bschroeder)\
**Post date:** [April 2, 2026, 11:13am UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/331 "2026-04-02T11:13:20Z")

</div>

That was the route I originally took, but I can't seem to access the path of a document tag via dynamic data. I'll keep playing around though and see if I can make it happen.

Note: This is all happening in a UDT as well, not on the UI layer.

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [May 15, 2026, 6:53am UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/332 "2026-05-15T06:53:29Z")

</div>

Hey @pturmel, I've got the latest as of writing installed (2.1.4) in 8.3.6, and `pageVarMap` isn't showing up in the functions list and it also isn't highlighting as if it's a recognised function, however it still returns a valid response:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/a/7/a7dea6f94b08d257fe43bf5299f55d691d7cd556.png)

![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/6/6/66bcb45e08e2da5a3c7876d00c5f68051151a06c.png)

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/2/8/28c41a7266feb9ebe287e04afb9a77853a3b1ce4.png)

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [May 15, 2026, 11:02am UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/333 "2026-05-15T11:02:44Z")

</div>

Hmmm. ~~Look for log entries for its registration getting rejected, and email me the backtrace.~~ If it works, it registered. I'll have to check its doc methods.

---

<div class="post-metadata">

**Author:** ![kyvski](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kyvski/32/85590_2.png) [@kyvski](https://forum.inductiveautomation.com/u/kyvski)\
**Post date:** [May 28, 2026, 6:52pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/334 "2026-05-28T18:52:01Z")

</div>

Trying forEach for the first time. Using it inside a tag expression and seeing odd behavior.

Is there a reason that it requires jsonEncode wrapping it to get the correct output?

For example,

```javascript
.stopCondNew = [0, 1, 8, ...]

forEach({[.]stopCondNew}, case(it(), 0, 'N/A', ... ,'UNK')) 

 -> causes "Bad Not Found" for the tag?

asList(forEach({[.]stopCondNew}, case(it(), 0, 'N/A', ... ,'UNK')))

 -> somewhat works, [["NA",...]]

jsonEncode(forEach({[.]stopCondNew}, case(it(), 0, 'N/A', ... ,'UNK'))) 

 -> works, returns ["NA",...]

```

These are all for type Document  
Why are we not able to use others, say Integer Array?  
The documentation for forEach says it returns PyList, but the conversion error says its ArrayList

Or with a simpler example:

```javascript
forEach(asList(1, 2, 3), it()) 
-> tag type is document: refuses to return a value
-> tag type is int array: conversion error (says its ArrayList)
-> tag type is String: shows [1, 2, 3] as a string

```

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [May 28, 2026, 8:14pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/335 "2026-05-28T20:14:43Z")

</div>

Yeah, you are hitting Document tag issues I've encountered, with fixes in my next release. Some breaking changes, so I'm been toying with it some more. Will release soon.

---

<div class="post-metadata">

**Author:** ![kyvski](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kyvski/32/85590_2.png) [@kyvski](https://forum.inductiveautomation.com/u/kyvski)\
**Post date:** [May 28, 2026, 8:22pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/336 "2026-05-28T20:22:08Z")

</div>

Would that fix the conversion issue as well, or do we have to use the Document type?  
Maybe I'm not understanding the return types correctly

And sounds good, will keep my eye out.  
Thanks Phil!

---

<div class="post-metadata">

**Author:** ![Brian\_Seater](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/brian_seater/32/82110_2.png) [@Brian\_Seater](https://forum.inductiveautomation.com/u/Brian_Seater)\
**Post date:** [June 26, 2026, 4:03pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/337 "2026-06-26T16:03:18Z")

</div>

Hi Phil,

I'm having an issue with the `orderBy()` expression function and different numeric types. I'm running Ignition 8.1.48 and Integration Toolkit 2.1.4.

The original setup was a script setting a `globalVarMap()` variable, and then an expression getting that value. Integers came out as either `Integer` or `BigInteger` depending on what the initial value was in the script.

`orderBy()` then throws an exception about not being able to cast between the two. It switches between a generic `java.lang.ClassCastException: null` and this one with a traceback:

```plaintext
java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.math.BigInteger (java.lang.Integer and java.math.BigInteger are in module java.base of loader 'bootstrap')
at java.base/java.math.BigInteger.compareTo(Unknown Source)
at java.base/java.util.Arrays.compare(Unknown Source)
at com.automation_pros.simaids.common.expressions.OrderBy$OrderableRow.compareTo(OrderBy.java:43)
at com.automation_pros.simaids.common.expressions.OrderBy$OrderableRow.compareTo(OrderBy.java:32)
at java.base/java.util.ComparableTimSort.countRunAndMakeAscending(Unknown Source)
at java.base/java.util.ComparableTimSort.sort(Unknown Source)
at java.base/java.util.Arrays.sort(Unknown Source)
at java.base/java.util.Arrays.sort(Unknown Source)
at java.base/java.util.ArrayList.sort(Unknown Source)
at com.automation_pros.simaids.common.expressions.OrderBy.postProcess(OrderBy.java:69)
at com.automation_pros.simaids.common.expressions.SimpleIterator.execute(SimpleIterator.java:223)
at com.inductiveautomation.ignition.common.expressions.FunctionExpression.execute(FunctionExpression.java:69)
at com.automation_pros.simaids.common.expressions.DebugMe.execute(DebugMe.java:47)
at com.inductiveautomation.ignition.common.expressions.FunctionExpression.execute(FunctionExpression.java:69)
at com.inductiveautomation.perspective.gateway.binding.PerspectiveExpression.execute(PerspectiveExpression.java:164)
at com.inductiveautomation.perspective.gateway.binding.PerspectiveExpression$EvaluationTask.runInternal(PerspectiveExpression.java:301)
at com.inductiveautomation.perspective.gateway.api.LoggingContext.mdc(LoggingContext.java:54)
at com.inductiveautomation.perspective.gateway.binding.PerspectiveExpression$EvaluationTask.run(PerspectiveExpression.java:280)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at com.inductiveautomation.perspective.gateway.threading.BlockingWork$BlockingWorkRunnable.run(BlockingWork.java:58)
at java.base/java.lang.Thread.run(Unknown Source)

```

Casting the values to a integer with either `int()` in the script or `toInt()` in the expression works as an immediate fix.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [June 26, 2026, 4:17pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/338 "2026-06-26T16:17:13Z")

</div>

Unfortunately, nothing I can really do about that. Keys for ordering must be comparable to each other, and java doesn't guarantee that for implementations of `Number`. That workaround is the right answer.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [June 30, 2026, 10:37pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/339 "2026-06-30T22:37:13Z")

</div>

Automation Professionals is pleased to announce new **Production Releases** of this module with the following new [utility functions](https://www.automation-pros.com/toolkit/doc/utility.html#runinclient):

- `system.tag.defineToolkitProp()`
- `system.tag.removeToolkitProp()`

For Ignition v8.3: [v2.1.5.261812214](https://www.automation-pros.com/ignition/Integration%20Toolkit-2.1.5.261812214-v83.modl)

For Ignition v8.1: [v2.1.5.261812141](https://www.automation-pros.com/ignition/Integration%20Toolkit-2.1.5.261812141-v81.modl)

The `defineToolkitProp()` function is intended to be called from a gateway startup event, once for each custom property the user wants to make available in the designer tag editor (with datatype predefined, optional default value, and other metadata). Unfortunately, the category must be a bundle key (for non-obvious reasons) and there is no runtime mechanism for adding bundle keys.  
☹

The `removeToolkitProp()` function is intended to be called by a designer (indirectly via `@RunInGateway`) during development of infrastructure that uses this feature.

The default category name is supplied by this module's main bundle.

An enhanced Spreadsheet Import Tool is ~~coming~~ [available](https://forum.inductiveautomation.com/t/spreadsheet-import-tool/93500/49) that takes advantage of this new feature.

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [July 1, 2026, 5:23am UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/340 "2026-07-01T05:23:11Z")

</div>

Sounds interesting. What are some examples of other category bundles? (or, where would I find a list?)

I assume you mean that defineToolkitProp would add custom props into these tag prop categories?

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/2/c/2c76eb0da6f46915a2d142d6d09ac5c576d3d160.png)

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [July 1, 2026, 5:44am UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/341 "2026-07-01T05:44:41Z")

</div>

> [@pturmel](#):
>
> Unfortunately, the category must be a bundle key (for non-obvious reasons) and there is no runtime mechanism for adding bundle keys.

`LocalizedString.createRaw`?

> **[createRaw - LocalizedString](https://sdk.inductiveautomation.com/javadoc/ignition83/8.3.1/com/inductiveautomation/ignition/common/i18n/LocalizedString.html#createRaw(java.lang.String))**
>
> declaration: package: com.inductiveautomation.ignition.common.i18n, class: LocalizedString

[Previous page](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934.md?page=16)

[Next page](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934.md?page=18)
