httpClient async promise.whenComplete() swallows errors

Platforms tested: Ignition 8.0.12 and 8.0.13.

I was using the asynchronous methods with the new http client provided in Ignition 8 via system.net.httpClient(). The promise returned from the async methods has a whenComplete() method. When I use a callback with whenComplete() any errors/exceptions that occur in the callback are swallowed. I expected that errors would be recorded but instead they are never shown in the gateway log or wrapper log. There is no indication that an error occurred. It is as if code execution ceases at the point of the error.

Here is an example script:

client=system.net.httpClient()
promise=client.getAsync('https://httpbin.org/get')

def got_response(response, error):
    logger=system.util.getLogger('temp')
    logger.info('before the exception') # shows in the logs
    raise Exception('error') # not shown in any logs
    logger.info('after the exception') # never executed

promise.whenComplete(got_response)

I was extremely confused when some of my code in the callback never ran. Eventually I figured out that errors were being swallowed and had to use something like this:

from java.lang import Throwable
logger=system.util.getLogger('temp')
try:
    #if I want to detect errors, I have to put my code in here
except BaseException as e:
    logger.error('python exception: %s' % e)
except Throwable as e:
    logger.error('java throwable: %s' % e)

In my case I had to match on Throwable in addition to Python’s BaseException since the error I was encountering was a Java Exception.

I’m assuming that the behavior of swallowing errors is a bug rather than being intended. Can someone at Inductive Automation open a bug for this?

You should not raise an exception inside the callback. At best we can log it, but you won’t be able to handle it.

edit: that is, you should not run code that can throw in the callbacks, and if you do you should catch it yourself. But we can probably add our own fallback that at least logs when this happens.

The code is an example. I explicitly raised an exception so it would be easy to reproduce the problem.

The real situation was that I was calling system.perspective.sendMessage() in the callback, but nothing happened because that function was throwing an IllegalArgumentException and that error was swallowed. It wasn’t until I added explicit error handling that I could see that I needed to change my sendMessage() call to include the session id and the page id since there was no “current session id” or “current page id” in the separate thread.

Are you saying that in that circumstance I should have had explicit exception handling because system.perspective.sendMessage() could throw an exception?

I think ultimately we should catch and log uncaught exceptions. I opened a ticket about it.

Awesome, thanks.

This is fixed in 8.0.16 - thrown errors from handlers should be true Python exceptions, and errors in handlers will be logged to a generic http client logger if not caught.

1 Like