Why does Ignition 8.3 sendRequest throw ClassCastException?

Daniel Price8 min read
HMI / SCADAOther ManufacturerTroubleshooting
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

After an upgrade to Ignition 8.3, system.util.sendRequest() called from the Designer script console fails before the message handler ever runs. It fails whether remoteServer points at another gateway or at the local gateway's own system name. The handler RequestProgram contains nothing but return True, and the same code worked on the previous major version. The root exception is:

java.lang.ClassCastException: class java.util.Collections$UnmodifiableMap cannot be cast to class org.python.core.PyDictionary
  at com.inductiveautomation.ignition.gateway.script.rpc.ScriptMessagingRpcImpl.extractPayload(ScriptMessagingRpcImpl.java:93)
  at com.inductiveautomation.ignition.gateway.script.rpc.ScriptMessagingRpcImpl.dispatchRequest(ScriptMessagingRpcImpl.java:69)

This is a defect in 8.3 script messaging. Neither your payload nor your handler causes it. The workaround is to restart both the gateway and the Designer. After that, the call works consistently until someone changes the gateway's Script Message Encoding setting. A permanent fix is targeted for 8.3.1, or 8.3.2 if it misses that release.

Where along the request path does the ClassCastException fire?

Read the stack from the bottom Caused by upward and map each frame to a hop. The request starts in the Designer's Jython console. It crosses the Designer-to-gateway RPC channel, gets dispatched on the gateway, and would only then be routed to the target project's message handler, locally or across the Gateway Network.

Hop Frame in the trace What happens there
1. Caller (Designer) JythonConsole$ConsoleWorker.doInBackground Script console executes your call
2. Scripting function SystemUtilities.sendRequest(SystemUtilities.java:827) Sends the request and blocks on CompletableFuture.get() for the reply
3. Transport to gateway RouteGroupImpl.service → RpcRoutes.handle → RpcDelegate$DelegateRpcHandler.handle Gateway receives the Designer RPC over its data routes
4. Gateway dispatch ScriptMessagingRpcImpl.dispatchRequest(...:69) Gateway starts dispatching the script message
5. Payload decode ScriptMessagingRpcImpl.extractPayload(...:93) Stops here. The decoded payload is a Java UnmodifiableMap, and the code casts it to a Jython PyDictionary
6. Routing to remoteServer not reached Gateway Network hop never happens
7. Message handler not reached return True never executes

The exception is created on the gateway at hop 5. It travels back to the Designer inside the RPC response and surfaces at hop 2, which is why sendRequest throws ExecutionException wrapped around another ExecutionException.

Check: confirm your trace contains ScriptMessagingRpcImpl.extractPayload as the innermost frame. If the innermost cause is somewhere else, such as inside your handler code or a Gateway Network connection error, you have a different problem and this procedure does not apply.

Is the failure immediate, or is it a timeout?

The call sets timeoutSec = 120. A handler that never answers, or an unreachable remote gateway, makes sendRequest wait for the timeout and fail with a timeout error. This defect returns at once, because the gateway rejects the request while decoding it.

Observation Likely cause Next step
Returns in well under timeoutSec with ClassCastException ... UnmodifiableMap ... PyDictionary at extractPayload 8.3 script-message payload decode defect Restart workaround (below)
Waits the full timeoutSec, then fails Handler blocked, or remote gateway or Gateway Network path unavailable Check Gateway Network connection status and handler logic
Error names a missing project or handler Wrong project or messageHandler string, or handler not saved Compare names character by character with the gateway event script config
Exception raised inside handler code Handler script error Read the gateway log for the handler's traceback

Check: time the call. A near-instant failure with the cast exception confirms you are dealing with the decode defect.

Can the remote gateway or the handler be ruled out?

Yes. Two facts from this installation isolate the fault to the first gateway hop:

  • The handler body is return True. Nothing in it can raise a cast exception, and the trace shows it never ran.
  • Pointing remoteServer at the local gateway, using system.tag.readBlocking("[System]Gateway/SystemName")[0].value and system.util.getProjectName(), fails identically. With the target set to the local gateway, the Gateway Network is not involved.

Rewriting the payload on the sending side will not help. Wrapping it in dict(), converting it to a string, or building it differently changes nothing, because the cast failure happens during gateway-side decoding. A plain Python dict of {"program": ..., "machine_id": ...} is the correct payload form for sendRequest.

Check: run the local-gateway variant from the script console. If it throws the same extractPayload exception, stop troubleshooting the Gateway Network and the remote project.

What does the Designer log show at a fresh launch?

The Designer retrieves the gateway's script message encoding when it starts. If that retrieval fails, the Designer logs Failed to retrieve script message encoding, defaulting to JSON near the start of its output. Use this message to decide which failure branch you are on.

  1. Close every Designer session connected to the gateway.
  2. Launch the Designer fresh and open the project.
  3. Open the Designer's log output and read from the first entries. Look for Failed to retrieve script message encoding, defaulting to JSON.
  4. Run the sendRequest test from the script console.
Log at launch sendRequest result Interpretation
Encoding warning present Cast exception Designer fell back to JSON while the gateway expects a different encoding. Capture the full stack trace under that warning for the vendor support case.
No encoding warning Cast exception Same defect, triggered without a visible retrieval failure. This was the case on this installation. Go to the restart procedure.
No encoding warning Handler response returned Designer and gateway agree on encoding. The defect is not active right now.

Check: record which row you are in before restarting anything. Once you restart, that evidence is gone.

How does the Script Message Encoding setting trigger the fault?

The payload is serialized by the caller and deserialized by the gateway according to the configured Script Message Encoding in the gateway settings. The gateway's dispatch code expects the decoded payload to be a Jython PyDictionary, the same object type the handler receives as payload. When the sender and the gateway's decode path are out of step on encoding, the gateway produces a read-only Java map (Collections$UnmodifiableMap) instead. The hard cast at ScriptMessagingRpcImpl.java:93 then throws.

A restart of both the gateway and the Designer puts the two back in step. For that reason, treat the Script Message Encoding property as frozen on 8.3.0. Any change to it reopens the mismatch until both sides restart again.

Check: in the gateway settings, note the current Script Message Encoding value and whether anyone changed it since the last gateway restart. If it changed after the restart, the fault is expected to return.

How do I restore sendRequest with the restart workaround?

  1. Schedule a window. A gateway restart drops Perspective and Vision sessions, tag providers, and device connections for its duration.
  2. Leave the Script Message Encoding property alone. Do not change it before or after the restart.
  3. Restart the gateway that receives the Designer's RPC, which is the gateway the Designer is logged into.
  4. Wait until the gateway reports running and the project's gateway event scripts are loaded.
  5. Close all open Designer instances, then relaunch the Designer so it picks up the encoding from the freshly started gateway.
  6. If the target in remoteServer is a different 8.3 gateway on which the encoding setting was changed, restart that gateway as well, so both ends of the Gateway Network hop run on their current settings.
  7. Re-run the local-gateway test first, then the remote test.

As a diagnostic, run the same call from a gateway-scoped script such as a gateway timer or tag change script, and log the result. If the gateway-scoped call succeeds while the Designer call fails, the fault is confined to the Designer-to-gateway RPC hop, which matches the frames in the trace.

Check: the script console returns True from the local-gateway test with no exception. Only then move to the remote target.

Which Ignition release carries the permanent fix?

The root cause has been identified and a fix written. It is targeted for 8.3.1, and for 8.3.2 if it does not make 8.3.1. Before you rely on either release, read its release notes for a script-messaging or sendRequest payload fix. Then prove it on a test gateway:

  1. Upgrade the test gateway and launch a fresh Designer.
  2. Run the round-trip test and confirm it passes.
  3. Change the Script Message Encoding property and save, without restarting the gateway or the Designer.
  4. Run the test again. If it still passes, the defect is fixed in that build. If the cast exception returns, keep the restart workaround and the frozen-setting rule in place.

Check: the test in step 4 passes on the upgraded build before you upgrade production gateways that depend on sendRequest.

How do I verify the full round trip from caller to handler?

Replace the bare return True with a handler that echoes the payload. That proves the payload arrived as a readable dict and that the return path works.

# Gateway Event Scripts > Message > RequestProgram
def handleMessage(payload):
    return {
        "ok": True,
        "program": payload["program"],
        "machine_id": payload["machine_id"]
    }

Test Pass condition
Local gateway (no remoteServer) Returns a dict with ok = True and the same program and machine_id that were sent
Remote gateway (remoteServer = "GatewayName") Same echoed dict, returned well inside timeoutSec
Designer log after test No ClassCastException and no Failed to retrieve script message encoding entry
Gateway log after test No ScriptMessagingRpcImpl errors

Run the same test from the production call site, whether that is a project script, a Perspective event, or a gateway script, with real program and machine_id values. Confirm the returned response is consumed as a dict by the calling code.

FAQ

How do I fix the UnmodifiableMap cannot be cast to PyDictionary error in Ignition 8.3 sendRequest?

Restart the gateway, then close and relaunch every Designer. The call works consistently afterward as long as the gateway's Script Message Encoding setting is not changed. The permanent fix is targeted for 8.3.1 or 8.3.2.

How do I tell whether the sendRequest failure is in my message handler or in the gateway?

Check the innermost frame of the trace. If it is ScriptMessagingRpcImpl.extractPayload, the gateway rejected the payload before routing, and your handler never ran. A handler bug shows its own traceback in the gateway log instead.

How do I test sendRequest without the Gateway Network involved?

Omit remoteServer, or set it to the local gateway's system name, and call a handler that echoes the payload. If that local call fails with the same cast exception, the fault is on the Designer-to-gateway hop rather than on the remote gateway.

Back to blog