No Protobuf serializer found for collection item class 'java.util.Collections$UnmodifiableMap

I'm trying to call a function on another gateway (system.tag.query) via system.util.sendRequest due to a bug (in 8.3.3?) where trying to do this on the local gateway on a remote tag provider and including alarms in the query goes out on a timeout (i've tried setting it to 10mins).

However now I'm running into this error:

Java Traceback:
Traceback (most recent call last):
  File "<input>", line 62, in <module>
  File "<module:shared.tag>", line 19, in backendTagQuery
	at java.base/java.util.concurrent.CompletableFuture.reportGet(Unknown Source)

	at java.base/java.util.concurrent.CompletableFuture.get(Unknown Source)

	at com.inductiveautomation.ignition.common.script.builtin.SystemUtilities.sendRequest(SystemUtilities.java:815)

	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)

java.util.concurrent.ExecutionException: java.util.concurrent.ExecutionException: com.inductiveautomation.ignition.common.rpc.proto.ProtoWrappedException: com.inductiveautomation.ignition.common.model.exception.ProtobufSerializationException: No Protobuf serializer found for collection item class 'java.util.Collections$UnmodifiableMap'


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

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

	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:192)

	at com.inductiveautomation.ignition.common.script.ScriptManager$ReflectedInstanceFunction.__call__(ScriptManager.java:561)

	at org.python.core.PyObject.__call__(PyObject.java:400)

	at org.python.pycode._pyx70.backendTagQuery$3(<module:shared.tag>:19)

	at org.python.pycode._pyx70.call_function(<module:shared.tag>)

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

	at org.python.core.PyBaseCode.call(PyBaseCode.java:306)

	at org.python.core.PyBaseCode.call(PyBaseCode.java:158)

	at org.python.core.PyFunction.__call__(PyFunction.java:437)

	at org.python.pycode._pyx69.f$0(<input>:106)

	at org.python.pycode._pyx69.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)

Caused by: java.util.concurrent.ExecutionException: com.inductiveautomation.ignition.common.rpc.proto.ProtoWrappedException: com.inductiveautomation.ignition.common.model.exception.ProtobufSerializationException: No Protobuf serializer found for collection item class 'java.util.Collections$UnmodifiableMap'

	at java.base/java.util.concurrent.CompletableFuture.reportGet(Unknown Source)

	at java.base/java.util.concurrent.CompletableFuture.get(Unknown Source)

	at com.inductiveautomation.ignition.common.script.builtin.SystemUtilities.sendRequest(SystemUtilities.java:815)

	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 org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:190)

	... 24 more

Caused by: com.inductiveautomation.ignition.common.rpc.proto.ProtoWrappedException: com.inductiveautomation.ignition.common.model.exception.ProtobufSerializationException: No Protobuf serializer found for collection item class 'java.util.Collections$UnmodifiableMap'

	at java.util.concurrent.CompletableFuture.reportGet(:0)

	at java.util.concurrent.CompletableFuture.get(:0)

	at com.inductiveautomation.ignition.common.script.message.SendRequestHandle.get(SendRequestHandle.java:63)

	at com.inductiveautomation.ignition.gateway.script.rpc.ScriptMessagingRpcImpl.dispatchRequest(ScriptMessagingRpcImpl.java:117)

	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(:0)

	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(:0)

	at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(:0)

	at java.lang.reflect.Method.invoke(:0)

	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.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.lang.Thread.run(:0)

Caused by: com.inductiveautomation.ignition.common.model.exception.ProtobufSerializationException: No Protobuf serializer found for collection item class 'java.util.Collections$UnmodifiableMap'

	at com.inductiveautomation.metro.impl.protoserializers.ProtobufUtil.packAnyToList(ProtobufUtil.java:186)

	at com.inductiveautomation.metro.impl.protoserializers.generic.collection.ListSerializer.toProtobufMessage(ListSerializer.java:46)

	at com.inductiveautomation.metro.impl.protoserializers.generic.collection.ListSerializer.toProtobufMessage(ListSerializer.java:21)

	at com.inductiveautomation.metro.impl.protoserializers.AnyObjectSerializer.toProtobufMessage(AnyObjectSerializer.java:69)

	at com.inductiveautomation.metro.impl.protoserializers.generic.MapSerializer.toProtobufMessage(MapSerializer.java:67)

	at com.inductiveautomation.metro.impl.protoserializers.generic.MapSerializer.toProtobufMessage(MapSerializer.java:23)

	at com.inductiveautomation.metro.impl.protoserializers.AnyObjectSerializer.toProtobufMessage(AnyObjectSerializer.java:69)

	at com.inductiveautomation.metro.impl.protoserializers.generic.MapSerializer.toProtobufMessage(MapSerializer.java:67)

	at com.inductiveautomation.metro.impl.protoserializers.generic.MapSerializer.toProtobufMessage(MapSerializer.java:23)

	at com.inductiveautomation.metro.impl.protoserializers.AnyObjectSerializer.toProtobufMessage(AnyObjectSerializer.java:69)

	at com.inductiveautomation.metro.impl.protoserializers.generic.MapSerializer.toProtobufMessage(MapSerializer.java:67)

	at com.inductiveautomation.metro.impl.protoserializers.generic.MapSerializer.toProtobufMessage(MapSerializer.java:23)

	at com.inductiveautomation.metro.impl.protoserializers.AnyObjectSerializer.toProtobufMessage(AnyObjectSerializer.java:69)

	at com.inductiveautomation.ignition.gateway.gan.protoserializers.PyDictionarySerializer.toProtobufMessage(PyDictionarySerializer.java:42)

	at com.inductiveautomation.ignition.gateway.gan.protoserializers.PyObjectSerializer.toProtobufMessage(PyObjectSerializer.java:95)

	at com.inductiveautomation.ignition.gateway.gan.protoserializers.RemoteScriptMsgSerializer.toProtobufMessage(RemoteScriptMsgSerializer.java:43)

	at com.inductiveautomation.ignition.gateway.gan.protoserializers.RemoteScriptMsgSerializer.toProtobufMessage(RemoteScriptMsgSerializer.java:13)

	at com.inductiveautomation.metro.impl.protoserializers.ServerMessageSerializer.toProtobufMessage(ServerMessageSerializer.java:47)

	at com.inductiveautomation.metro.impl.transport.ProtobufDirectAdapter.adaptData(ProtobufDirectAdapter.java:79)

	at com.inductiveautomation.metro.impl.transport.ProtobufDirectAdapter.adaptData(ProtobufDirectAdapter.java:29)

	at com.inductiveautomation.metro.impl.transport.AbstractTransportAdapter.handle(AbstractTransportAdapter.java:59)

	at com.inductiveautomation.metro.impl.MessageSendManager.sendNext(MessageSendManager.java:233)

	at java.util.concurrent.Executors$RunnableAdapter.call(:0)

	at java.util.concurrent.FutureTask.run(:0)

	at java.util.concurrent.ThreadPoolExecutor.runWorker(:0)

	at java.util.concurrent.ThreadPoolExecutor$Worker.run(:0)

	... 1 more

Traceback (most recent call last):
  File "<input>", line 62, in <module>
  File "<module:shared.tag>", line 19, in backendTagQuery
	at java.base/java.util.concurrent.CompletableFuture.reportGet(Unknown Source)

	at java.base/java.util.concurrent.CompletableFuture.get(Unknown Source)

	at com.inductiveautomation.ignition.common.script.builtin.SystemUtilities.sendRequest(SystemUtilities.java:815)

	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)

java.util.concurrent.ExecutionException: java.util.concurrent.ExecutionException: com.inductiveautomation.ignition.common.rpc.proto.ProtoWrappedException: com.inductiveautomation.ignition.common.model.exception.ProtobufSerializationException: No Protobuf serializer found for collection item class 'java.util.Collections$UnmodifiableMap'
>>> 
Java Traceback:
Traceback (most recent call last):
  File "<input>", line 62, in <module>
  File "<module:shared.tag>", line 19, in backendTagQuery
	at java.base/java.util.concurrent.CompletableFuture.reportGet(Unknown Source)

	at java.base/java.util.concurrent.CompletableFuture.get(Unknown Source)

	at com.inductiveautomation.ignition.common.script.builtin.SystemUtilities.sendRequest(SystemUtilities.java:815)

	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)

java.util.concurrent.ExecutionException: java.util.concurrent.ExecutionException: com.inductiveautomation.ignition.common.rpc.proto.ProtoWrappedException: com.inductiveautomation.ignition.common.model.exception.ProtobufSerializationException: No Protobuf serializer found for collection item class 'java.util.Collections$UnmodifiableMap'


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

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

	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:192)

	at com.inductiveautomation.ignition.common.script.ScriptManager$ReflectedInstanceFunction.__call__(ScriptManager.java:561)

	at org.python.core.PyObject.__call__(PyObject.java:400)

	at org.python.pycode._pyx74.backendTagQuery$3(<module:shared.tag>:19)

	at org.python.pycode._pyx74.call_function(<module:shared.tag>)

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

	at org.python.core.PyBaseCode.call(PyBaseCode.java:306)

	at org.python.core.PyBaseCode.call(PyBaseCode.java:158)

	at org.python.core.PyFunction.__call__(PyFunction.java:437)

	at org.python.pycode._pyx73.f$0(<input>:106)

	at org.python.pycode._pyx73.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)

Caused by: java.util.concurrent.ExecutionException: com.inductiveautomation.ignition.common.rpc.proto.ProtoWrappedException: com.inductiveautomation.ignition.common.model.exception.ProtobufSerializationException: No Protobuf serializer found for collection item class 'java.util.Collections$UnmodifiableMap'

	at java.base/java.util.concurrent.CompletableFuture.reportGet(Unknown Source)

	at java.base/java.util.concurrent.CompletableFuture.get(Unknown Source)

	at com.inductiveautomation.ignition.common.script.builtin.SystemUtilities.sendRequest(SystemUtilities.java:815)

	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 org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:190)

	... 24 more

Caused by: com.inductiveautomation.ignition.common.rpc.proto.ProtoWrappedException: com.inductiveautomation.ignition.common.model.exception.ProtobufSerializationException: No Protobuf serializer found for collection item class 'java.util.Collections$UnmodifiableMap'

	at java.util.concurrent.CompletableFuture.reportGet(:0)

	at java.util.concurrent.CompletableFuture.get(:0)

	at com.inductiveautomation.ignition.common.script.message.SendRequestHandle.get(SendRequestHandle.java:63)

	at com.inductiveautomation.ignition.gateway.script.rpc.ScriptMessagingRpcImpl.dispatchRequest(ScriptMessagingRpcImpl.java:117)

	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(:0)

	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(:0)

	at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(:0)

	at java.lang.reflect.Method.invoke(:0)

	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.lang.Thread.run(:0)

Caused by: com.inductiveautomation.ignition.common.model.exception.ProtobufSerializationException: No Protobuf serializer found for collection item class 'java.util.Collections$UnmodifiableMap'

	at com.inductiveautomation.metro.impl.protoserializers.ProtobufUtil.packAnyToList(ProtobufUtil.java:186)

	at com.inductiveautomation.metro.impl.protoserializers.generic.collection.ListSerializer.toProtobufMessage(ListSerializer.java:46)

	at com.inductiveautomation.metro.impl.protoserializers.generic.collection.ListSerializer.toProtobufMessage(ListSerializer.java:21)

	at com.inductiveautomation.metro.impl.protoserializers.AnyObjectSerializer.toProtobufMessage(AnyObjectSerializer.java:69)

	at com.inductiveautomation.metro.impl.protoserializers.generic.MapSerializer.toProtobufMessage(MapSerializer.java:67)

	at com.inductiveautomation.metro.impl.protoserializers.generic.MapSerializer.toProtobufMessage(MapSerializer.java:23)

	at com.inductiveautomation.metro.impl.protoserializers.AnyObjectSerializer.toProtobufMessage(AnyObjectSerializer.java:69)

	at com.inductiveautomation.metro.impl.protoserializers.generic.MapSerializer.toProtobufMessage(MapSerializer.java:67)

	at com.inductiveautomation.metro.impl.protoserializers.generic.MapSerializer.toProtobufMessage(MapSerializer.java:23)

	at com.inductiveautomation.metro.impl.protoserializers.AnyObjectSerializer.toProtobufMessage(AnyObjectSerializer.java:69)

	at com.inductiveautomation.metro.impl.protoserializers.generic.MapSerializer.toProtobufMessage(MapSerializer.java:67)

	at com.inductiveautomation.metro.impl.protoserializers.generic.MapSerializer.toProtobufMessage(MapSerializer.java:23)

	at com.inductiveautomation.metro.impl.protoserializers.AnyObjectSerializer.toProtobufMessage(AnyObjectSerializer.java:69)

	at com.inductiveautomation.ignition.gateway.gan.protoserializers.PyDictionarySerializer.toProtobufMessage(PyDictionarySerializer.java:42)

	at com.inductiveautomation.ignition.gateway.gan.protoserializers.PyObjectSerializer.toProtobufMessage(PyObjectSerializer.java:95)

	at com.inductiveautomation.ignition.gateway.gan.protoserializers.RemoteScriptMsgSerializer.toProtobufMessage(RemoteScriptMsgSerializer.java:43)

	at com.inductiveautomation.ignition.gateway.gan.protoserializers.RemoteScriptMsgSerializer.toProtobufMessage(RemoteScriptMsgSerializer.java:13)

	at com.inductiveautomation.metro.impl.protoserializers.ServerMessageSerializer.toProtobufMessage(ServerMessageSerializer.java:47)

	at com.inductiveautomation.metro.impl.transport.ProtobufDirectAdapter.adaptData(ProtobufDirectAdapter.java:79)

	at com.inductiveautomation.metro.impl.transport.ProtobufDirectAdapter.adaptData(ProtobufDirectAdapter.java:29)

	at com.inductiveautomation.metro.impl.transport.AbstractTransportAdapter.handle(AbstractTransportAdapter.java:59)

	at com.inductiveautomation.metro.impl.MessageSendManager.sendNext(MessageSendManager.java:233)

	at java.util.concurrent.Executors$RunnableAdapter.call(:0)

	at java.util.concurrent.FutureTask.run(:0)

	at java.util.concurrent.ThreadPoolExecutor.runWorker(:0)

	at java.util.concurrent.ThreadPoolExecutor$Worker.run(:0)

	... 1 more

Traceback (most recent call last):
  File "<input>", line 62, in <module>
  File "<module:shared.tag>", line 19, in backendTagQuery
	at java.base/java.util.concurrent.CompletableFuture.reportGet(Unknown Source)

	at java.base/java.util.concurrent.CompletableFuture.get(Unknown Source)

	at com.inductiveautomation.ignition.common.script.builtin.SystemUtilities.sendRequest(SystemUtilities.java:815)

	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)

java.util.concurrent.ExecutionException: java.util.concurrent.ExecutionException: com.inductiveautomation.ignition.common.rpc.proto.ProtoWrappedException: com.inductiveautomation.ignition.common.model.exception.ProtobufSerializationException: No Protobuf serializer found for collection item class 'java.util.Collections$UnmodifiableMap'

This is my script:

def backendTagQuery(gwNumber, provider, query, limit=None, continuation=None):
	return system.util.sendRequest(  ##<--- This is line 19
		project="Tags",
		messageHandler="tagQuery",
		payload={'provider': provider, "query": query, "limit": limit, "continuation": continuation},
		remoteServer="IGN-GW{}-Backend".format(gwNumber),
		timeoutSec=300
	)

This is the "query" being provided:

query = {
    "options": {
        "includeUdtMembers": True,
        "includeUdtDefinitions": True
    },
    "condition": {
        "path": "SOLAR/*",
        "attributes": {
            "values": [],
            "requireAll": True
        },
        "properties": {
            "op": "Or",
            "conditions": [
                {
                    "op": "And",
                    "conditions": [
                        {
                            "prop": "tagType",
                            "comp": "Equal",
                            "value": "AtomicTag"
                        }
                    ]
                }
            ]
        }
    },
    "returnProperties": [
    	"alarms",
        "scaleFactor",
        "scaleMode",
        "opcItemPath",
        "dataType",
        "engHigh",
        "tooltip",
        "historyEnabled",
        "engLow",
        "engUnit"
    ]
}

More investigation.
It doesn't like the query param. If I set that to {} it "works".
It looks like it doesn't like the query["condition"]["properties"]["conditions"] list. If I remove this, it goes through.

As a workaround, you could json encode the query and decode in the remote handler. Don't be surprised if you get similar (but other) protobuf errors with the return value.

So I wasn't able to replicate your exact Protobuf error using 8.3.6 gateways. This was my setup:

You can let me know how close that was to yours. While I couldn't reproduce that exact error with Collections.UnmodifiableMap, I did hit a Protobuf error on the history-backend gateway when the query result contained alarm data. My exact error was com.inductiveautomation.ignition.common.model.exception.ProtobufSerializationException: Class 'com.inductiveautomation.ignition.common.alarming.config.BasicAlarmDefinition' and object 'Alarm' cannot be Protobuf serialized

I'll be writing up a ticket to deal with the alarm Protobuf issue. 8.3.5 had some gateway network Protobuf fixes, so your initial problem might have been fixed by those.

Any idea how to fix the Protobuf error when we get alarm results back?

I'm literally running a tag report to get the alarm data back and this is totally borking my functionality.

I've got the following going on:

  1. Open Vision popup to show repeater of all alarms defined on a tag UDT instance.

  2. Popup creates a tag query for the tagpath provided, and sends to the backend scripting project to process via "sendRequestAsync".
    The query pyDict is encoded as json, before putting it into a payload pyDict.

payload = {"description":"Alarm Details Popup", "provider":provider, "query":queryJSON, "limit":None}

  1. Backend message handler receives payload. Extracts the payload items. The query is decoded back into a pyobj.

  2. Backend script runs system.tag.query and gets "results" back.

  3. Try returning results back to the Vision popup results in the following error:

com.inductiveautomation.ignition.common.rpc.proto.ProtoSerializationException: Fallback serialization failed


I needed to encode the tag query result in a similar case - make that this exact case, I didn't see it was my topic! , but more than likely the json encode will fail due to "recursion" levels; not really, it just can't handle the java objects in the results, so you would need to first convert them into pyobj then encode

You mean loop through the returned objects and manually try convert all the different members?

Have you got a decent function you wouldn't mind sharing for that? Seems a bit involved.

Looking at whats coming back, could I simply try to cast anything with "com.inductiveautomation" type as str?

I imagine these would be fine.. but seems a bit janky.

com.inductiveautomation.ignition.common.tags.paths.BasicTagPath
com.inductiveautomation.ignition.common.sqltags.model.types.DataType
com.inductiveautomation.ignition.common.sqltags.model.types.AlertAckMode
com.inductiveautomation.ignition.common.tags.config.types.TagObjectType
from com.inductiveautomation.ignition.common.auth.permissions import PermissionsConfig
from com.inductiveautomation.ignition.common.config import BoundValue
from com.inductiveautomation.ignition.common import BasicDataset
from com.inductiveautomation.ignition.common.tags.config.properties import ParameterValue


def sanitise(element):
	"""
	Recursively convert Ignition-native objects into plain Python primitives
	suitable for writing into a spreadsheet cell or for jsonEncode downstream.

	jsonEncode trips over several common Ignition types
	(PermissionsConfig, BoundValue, BasicDataset, ParameterValue, ...).
	"""
	# Preserve None as-is.  Without this, None falls through every branch
	# below and ends up stringified to "None" by the final str() fallback,
	# which then masquerades as a truthy non-dict and breaks downstream
	# `params or {}` / `.keys()` / `.items()` patterns.
	if element is None:
		return None

	# Recurse into containers first.  dict-likes are detected by .keys();
	# other iterables become lists.  Strings are iterable but we *don't*
	# want to explode them char-by-char, so short-circuit those.
	if isinstance(element, basestring):
		return element
	if hasattr(element, '__iter__'):
		if hasattr(element, 'keys'):
			keys = list(element.keys())
			return dict((k, sanitise(element[k])) for k in keys)
		return [sanitise(x) for x in element]

	# Scalars / known wrapper types.
	if isinstance(element, (int, float, bool)):
		return element
	if isinstance(element, PermissionsConfig):
		return {
			'type': str(element.type),
			'securityLevels': sanitise(element.securityLevels),
		}
	if isinstance(element, BoundValue):
		return {
			'bindType': str(element.bindType),
			'binding': sanitise(element.binding),
		}
	if isinstance(element, BasicDataset):
		pds = system.dataset.toPyDataSet(element)
		return [dict(zip(pds.columnNames, list(row))) for row in pds]
	if isinstance(element, ParameterValue):
		return {
			'datatype': str(element.datatype),
			'value': sanitise(element.value),
		}

	# Unknown / opaque type: stringify rather than crash.
	return str(element)

Do you know exactly which system couldn't serialize the message? Was it the gateway-to-gateway transfer or the gateway-to-designer/Vision client transfer? Also, do you happen to know the class of the object that couldn't be serialized?

Hi, it was the Gateway (message handler ) to Vision client transfer.

And it was the entirety of a system.tag.query results object. In my case one result, so a list of 1.

I tried simply returning the whole thing from the message handler back to the calling function and it failed.

So I've had to use the sanitisation function above to be able to return it back happily.

You can opt-in to Java serialization for this path:

If you don't, then you're basically not allowed to send arbitrary types, so the sanitise approach is needed. It's going to be an endless whack-a-mole though.
Basically the difference is that Java serialization is "open world" by default - it encodes anything and everything as-it-comes-in, and expects the other side to know how to deal with it. Great for convenience, terrible for security.
Almost all of our newer serialization strategies throughout the platform flip to a "closed world" - you have to know ahead of time what's going to be on both sides of the wire and account for it.

Is this something that's mentioned in the upgrade guide? Not just that it went from java serialisation to protobuf, but the implications of it on your existing code?

Upgraded systems continue using Java serialization for script messaging, specifically so that your code doesn't break in edge case situations like this. Only new systems default to protobuf.

EDIT: Though to be clear, I'd definitely recommend moving to protobuf if you can. In the happy path, you don't notice any difference and things are both faster and safer.
In the less happy path, you get to deal with workarounds for the specific edge cases but don't have to fix every problem.

No that's what winds me up. I made sure to thoroughly look through all the upgrade guide before and after upgrading. This is all that was mentioned (below).

The implications were far more reaching than just Gateway Network comms.

Would have been good to know that before we pulled the trigger, I would have waited for a more stable release otherwise.

Protobuf and Java Serialization Within the Gateway Network

Due to security and configuration persistence updates, Ignition 8.3 no longer uses Java serialization to convert your data to a saveable state, but instead relies on Protobuf. When upgrading or restoring an 8.3 Gateway, the webserver settings will enable the Allow Java Serialization property automatically. In the Gateway Network, there will be a banner on the Gateway expecting an incoming connection warning you that this property is enabled.

Keep in mind that Allow Java Serialization must be enabled if you add 8.3 Gateways to your architecture containing 8.1 Gateways.

Ok well a bit more documentation is required to explain that perhaps, because the only info was about Gateway network issues on different versions of Ignition, and this ominous warning when the setting is left as Java.

Insecure Message Format

Java Serialization has unavoidable security risks and should only be used for backwards compatibility. Protobuf encoding should be used for messaging between Clients, Designers and the Gateway.

So you can probably appreciate why I decided to try use Protobuf.

Will probably try to stick to it for now, now that I know what is required to fix if things are reported to break.

I think I did this as well, I saw that warning and I changed it from Java serialisation, not realising the consequence and impact on the project