# \[Bug\] system.util.sendMessage fails with remoteServers when payload is not a dict

**URL:** <https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040>\
**Category:** Ignition\
**Tags:** ignition81\
**Created:** [January 14, 2022, 6:57pm UTC](https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040 "2022-01-14T18:57:23Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![alan.s.kinnaman](https://avatars.discourse-cdn.com/v4/letter/a/4af34b/32.png) [@alan.s.kinnaman](https://forum.inductiveautomation.com/u/alan.s.kinnaman)\
**Post date:** [January 14, 2022, 6:57pm UTC](https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040/1 "2022-01-14T18:57:23Z")

</div>

In Ignition 8.1.10, when calling `system.util.sendMessage`, if using `scope=G` and a value is specified for `remoteServers`, the message is never received on the remote server if `payload` is an Ignition type, such as `com.inductiveautomation.perspective.gateway.script.PropertyTreeScriptWrapper$ObjectWrapper`. I need to first convert the payload to a Python `dict` before sending the message.

This does not appear to be the case using `scope=S` if `remoteServers` is _not_ specified. In that case, the message is sent successfully, even when `payload` is not a true `dict`.

To summarize, this works:

```python
system.util.sendMessage(
		project='my-project',
		messageHandler='my-message',
		payload=self.custom.myCustomParam,
		scope='S')

```

But this does not:

```python
system.util.sendMessage(
		project='my-project',
		messageHandler='my-message',
		payload=self.custom.myCustomParam,
		scope='G',
		remoteServers=myRemoteServerAddress)

```

---

<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:** [January 14, 2022, 7:26pm UTC](https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040/2 "2022-01-14T19:26:42Z")

</div>

Not terribly surprising, since Perspective runs in its own classloader (like all modules). Any scope other than ‘S’ won’t be able to deserialize a Perspective class instance (deserializing being required for objects passed through a network).

Not a bug, but a side effect of Ignition’s module isolation.

---

<div class="post-metadata">

**Author:** ![alan.s.kinnaman](https://avatars.discourse-cdn.com/v4/letter/a/4af34b/32.png) [@alan.s.kinnaman](https://forum.inductiveautomation.com/u/alan.s.kinnaman)\
**Post date:** [January 14, 2022, 8:07pm UTC](https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040/3 "2022-01-14T20:07:51Z")

</div>

Makes perfect sense. Still, this behavior was unexpected, and it was difficult to figure out what was going wrong.

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [January 14, 2022, 8:13pm UTC](https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040/4 "2022-01-14T20:13:11Z")

</div>

I think there’s an open ticket on our end about the Perspective wrapper objects “leaking” into scripting.

IGN-1660 if anyone else from IA is playing along. I’m going to add this thread to the ticket.

---

<div class="post-metadata">

**Author:** ![richardNZ15](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/richardnz15/32/33373_2.png) [@richardNZ15](https://forum.inductiveautomation.com/u/richardNZ15)\
**Post date:** [July 5, 2022, 4:50am UTC](https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040/5 "2022-07-05T04:50:32Z")

</div>

What is the solution for this in general terms when trying to use Perspective properties as pyDicts?  
I’m trying to send custom parameters as pyDicts but not sure how to convert “” type into a proper python dictionary type.

EDIT: Seems like just casting it with dict(objectRefHere) did the trick for what i was trying to do.

---

<div class="post-metadata">

**Author:** ![Trent.Boudreaux](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/trent.boudreaux/32/4138_2.png) [@Trent.Boudreaux](https://forum.inductiveautomation.com/u/Trent.Boudreaux)\
**Post date:** [October 7, 2022, 3:25pm UTC](https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040/6 "2022-10-07T15:25:22Z")

</div>

Has there been any update on this behavior? Casting the object as a dictionary seems to be a decent workaround, but has to be done at every level of a nested list/dictionary object. I'm having to do a bit of type manipulation in perspective before calling message handlers quite often.

---

<div class="post-metadata">

**Author:** ![alan.s.kinnaman](https://avatars.discourse-cdn.com/v4/letter/a/4af34b/32.png) [@alan.s.kinnaman](https://forum.inductiveautomation.com/u/alan.s.kinnaman)\
**Post date:** [October 7, 2022, 7:13pm UTC](https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040/7 "2022-10-07T19:13:55Z")

</div>

We have the code below in a script module that converts Ignition-specific objects to native Python lists and dicts. We pass the Ignition object to the `propertyToPython` function, and get back Python lists and/or dicts.

```auto
def _isDictLikeObject(value):
	return 'iteritems' in dir(value)

def _isListLikeObject(value):
	return 'append' in dir(value)
	    	
	
def dictLikeObjectToDict(dictLikeObject):
	pyDict = {}
	for key, item in dict(dictLikeObject).iteritems():
		item = propertyToPython(item)				
		pyDict[key] = item
	return pyDict

def listLikeObjectToList(listLikeObject):
	pyList = []
	for item in list(listLikeObject):
		item = propertyToPython(item)				
		pyList.append(item)
	return pyList

def propertyToPython(property):
	"""
	Accepts a value, object, or array property that might be
	encoded as an Ignition ObjectWrapper or ArrayWrapper and
	returns the property as Python dicts and lists.
	"""
	if _isDictLikeObject(property):
		property = dictLikeObjectToDict(property)
	elif _isListLikeObject(property):
		property = listLikeObjectToList(property)
	return property

```

---

<div class="post-metadata">

**Author:** ![Trent.Boudreaux](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/trent.boudreaux/32/4138_2.png) [@Trent.Boudreaux](https://forum.inductiveautomation.com/u/Trent.Boudreaux)\
**Post date:** [October 7, 2022, 7:51pm UTC](https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040/8 "2022-10-07T19:51:19Z")

</div>

Thanks for sharing!

I landed on something similar

```auto
def unwrap(object): 
	if str(object).startswith('<ArrayWrapper>'):
		object = list(object)
		object = [unwrap(item) for item in object]
	elif str(object).startswith('<ObjectWrapper>'):
		object = dict(object)
		object = {key: unwrap(value) for key, value in object.items()}
	return object

```

Would prefer to explicitly check for ArrayWrapper and ObjectWrapper types, but not having any luck.  
\<type 'com.inductiveautomation.perspective.gateway.script.PropertyTreeScriptWrapper$ArrayWrapper'\>  
\<type 'com.inductiveautomation.perspective.gateway.script.PropertyTreeScriptWrapper$ObjectWrapper'\>

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [October 7, 2022, 7:54pm UTC](https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040/9 "2022-10-07T19:54:18Z")

</div>

Check out the `system.util.deepCopy()` function in the [Ignition Extensions](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615) module.

---

<div class="post-metadata">

**Author:** ![Trent.Boudreaux](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/trent.boudreaux/32/4138_2.png) [@Trent.Boudreaux](https://forum.inductiveautomation.com/u/Trent.Boudreaux)\
**Post date:** [October 7, 2022, 8:03pm UTC](https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040/10 "2022-10-07T20:03:31Z")

</div>

Thanks for the recommendation. The Ignition Extensions module sounds like a great idea!  
Unfortunately, I'm not able to load that module into my environment at the moment.  
I have tried python's [copy.deepcopy()](https://docs.python.org/3/library/copy.html) but it throws an error.

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [October 7, 2022, 8:26pm UTC](https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040/11 "2022-10-07T20:26:04Z")

</div>

Yeah, it pukes if it hits an object that it isn’t prepared for.

Here is a pure Jython wrapper that should help.

> [@Copy dictionary and modify it without affecting the original](https://forum.inductiveautomation.com/t/copy-dictionary-and-modify-it-without-affecting-the-original/62297/9):
>
> As @bkarabinchak.psi pointed out even though the structure “looks” like a dict it isn’t actually a dict. Actually the type of a perspective property when accessed from a script is:[com.inductiveautomation.perspective.gateway.script.PropertyTreeScriptWrapper.ObjectWrapper](https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.1/com/inductiveautomation/perspective/gateway/script/PropertyTreeScriptWrapper.ObjectWrapper.html). Thus deepcopy chokes when it runs into an object that it doesn’t know how to handle. If you need a true deepcopy, then you have couple of options. Write a function which recursively loops through the structure and correctl…

---

<div class="post-metadata">

**Author:** ![alan.s.kinnaman](https://avatars.discourse-cdn.com/v4/letter/a/4af34b/32.png) [@alan.s.kinnaman](https://forum.inductiveautomation.com/u/alan.s.kinnaman)\
**Post date:** [October 7, 2022, 8:53pm UTC](https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040/12 "2022-10-07T20:53:42Z")

</div>

I like this. It looks to be basically the same as our solution, but more concise.

A word of caution, though: An earlier implementation of our solution also checked for specific strings in the type names, similar to what you're doing with `<ArrayWrapper>` and `<ObjectWrapper>`, but that broke in an Ignition update at some point when Ignition changed the classes used in certain situations. That's why we settled on checking for the presence of the `iteritems` and `append` methods to determine if the object is a list or a dict.

---

<div class="post-metadata">

**Author:** ![Trent.Boudreaux](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/trent.boudreaux/32/4138_2.png) [@Trent.Boudreaux](https://forum.inductiveautomation.com/u/Trent.Boudreaux)\
**Post date:** [October 7, 2022, 11:08pm UTC](https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040/13 "2022-10-07T23:08:05Z")

</div>

great point, I'll use the in dir() logic that you provided. Thanks!

---

<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:** [October 7, 2022, 11:26pm UTC](https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040/14 "2022-10-07T23:26:54Z")

</div>

Consider using the python builtin `hasattr()` instead of `x in dir()`. Should be much faster.

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [October 7, 2022, 11:34pm UTC](https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040/15 "2022-10-07T23:34:28Z")

</div>

The script in the link I provided shows how to explicitly check for the types.

---

<div class="post-metadata">

**Author:** ![justin.brzozoski](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justin.brzozoski/32/13452_2.png) [@justin.brzozoski](https://forum.inductiveautomation.com/u/justin.brzozoski)\
**Post date:** [March 25, 2024, 4:43pm UTC](https://forum.inductiveautomation.com/t/bug-system-util-sendmessage-fails-with-remoteservers-when-payload-is-not-a-dict/55040/16 "2024-03-25T16:43:05Z")

</div>

For a more concise and resilient deep copy before serializing, try this.

> [@Function to convert json string or python dict into ObjectWrapper](https://forum.inductiveautomation.com/t/function-to-convert-json-string-or-python-dict-into-objectwrapper/32861/16):
>
> Forcing each to the expected type while traversing makes a deep copy: def sanitize\_tree(element): if hasattr(element, '\_\_iter\_\_'): if hasattr(element, 'keys'): return dict((k, sanitize\_tree(element[k])) for k in element.keys()) else: return list(sanitize\_tree(x) for x in element) return element EDIT: Got rid of the unnecessary zip() and used only one keys() iterator instead of two.

(Just stumbled on this thread while researching `remoteServers` and realized I knew a good answer to the question being asked even though it was a bit old... Figured I'd help anyone else who lands here later...)
