Two Types of Runtime Error?

Just trying to understand a bit more about errors in Corona.

I seem to have two different types of runtime errors.

The first is one that creates an error and a dialogue box on the simulator or the device.  The other just generates a runtime error in the Build Panel, but does not crash the device.

Both say the words Error: Runtime error.

I would rather the behavior was consistent.

I am going to test if the unhandled error event captures this “under the hood” error…

Anyone else come across these differences?

I can confirm that worryingly, these “other” Runtime errors do not get captured by 

local unhandledErrorListener = function( event )

    print( "We have a problem: " … event.errorMessage )

end

Runtime:addEventListener( “unhandledError”, unhandledErrorListener )

and do not crash the app.  Either on simulator or on the device.

An example of the error is: -

ERROR: Runtime error

\Development\Medication\3.x\creamInfoNew.lua:184: attempt to index field ‘?’ (a nil value)

The error in question is that at line 184 the code is trying to reference a table array which is actually not there.  However, why is the runtime error not crashing the app??

Found it - but I really need a solution.

This function, if called direct will generate a runtime error which present a dialogue box in the simulator and crashes the app: -

local test = {}

local test = function()

if tonumber(test[1].HasField) == 4 then

print(“test1”)

end

end

However, if this function is called as part of a Call Back Function following a network call (such as network.request) I only get the Runtime error in the output panel, but the app continues and does not crash…

I could really do with this behavior being consistent.  Any advice on how I can force the app to crash on a runtime error that may occur following a network call??

I note that in the API documentation there is this line: -

“Network requests do not raise runtime errors in the event of failure so there is no need to wrap them in a Lua pcall() statement (errors are reported in the event sent to the network listener).”

However, the error is not being generated by the network request.  The error is being generated by a function that is called after the network request is called and the call back is called e.g.: -

local PostResponse2 = function(SaveRep)

if (SaveRep.isError) then

 – OK - network called failed… raise an error

else

     – network call was OK - now do some more work

      test()

end

end

function scene:show(event)

http.MakeNetworkCall(PostResponse2,“GetCreamApplicationRecord”,1)

end

I have raised a Bug Report for this issue as I feel it is fundamental.  I don’t think it should be allowed that you can have a situation where a thread of code could result in a un-trappable runtime error.  Any call after a succesful network.request can fail and the app will continue.  I have a work around which is to wrap any subsequent call with a timer.performWithDelay.  Not sure if any of the moderators have a view on this one…

There are not two classes of errors… all errors will be trapped with** Runtime:addEventListener( “unhandledError”, unhandledErrorListener )** if the error is a true run time error.

Saying that all errors are not the same, some are trivial and handled within the run time, some are more important and need to be handled by the calling code.

Generally, it is up to the developer to handle errors in callback functions.  This is not a bug.  It is your responsibility to handle the post back and deal with it accordingly.  There is no way that a framework can possibly understand a callback of your network requests.  If your back-end service raises an error that will be included in the post back event.

The logic is that you will control the response to your network event and therefore you will also be responsible for your own error handling on your own response.  Makes perfect sense to me.

Sorry, that is not correct.  If you read my post above, the response has not generated any error.   The runtime error is not being generated by the response.  The runtime error is being generated by a function called after a successful response.

In my code example above if I run the code without a network response the system generates a Run Time error.  The app crashes and the error is displayed on the page.  The Runtime:addEventListener( “unhandledError”, unhandledErrorListener ) can catch the error.

If the same code is run, but with a network request as part of that call, same error is generated, the words Runtime Error is output to the build window, but the app does not crash, the app does not display any error and the Runtime:addEventListener will not catch it.

You may say that all network.requests have to be end points e.g. they take no further action.  However, practically this makes for a lot of unnecessary code, especially with a business app.

In my app before the scene loads I get some data from our cloud server.  I don’t want the app to do anything else until the data has been retrieved, as dependent upon the data the screen may need to present data in a certain way.  The logical way of managing this is by having a function or set of functions called following a successful network request.  But, with this version of Corona, if any of those subsequent functions has some dodgy code or if the data is corrupted and causes a runtime error the app will not crash.  The app just carries on, potentially in a very unstable state.

I have a work around, but I have a load of revisions to make to implement it.

My work around is to wrap any subsequent calls after a successful response with a timer.performWithDelay().  Here is an example of how the above code now works with the new wrapper - runtime error is now generated as it should be: -

Please - can you run the below code and just see the treatment.  I would really appreciate it.

local test = {} local test = function() if tonumber(test[1].HasField) == 4 then print("test1") end end local function networkListener( event )     if ( event.isError ) then         print( "Network error: ", event.response )     else -- toggle these two commands to see the different treatment of the error        -- test()         timer.performWithDelay(1, function() return test() end)     end end    -- Access Google  network.request( "http://www.google.com", "GET", networkListener )

Curious… both raised an error (as expected) but there is a difference if it’s raised inline or via a timer.  

There should be no difference between how the error is raised and a global catch all should fire but it doesn’t.

This is the most simple use case where there is no “ERROR” printed

function unhandledErrorListener( event )   print("ERROR") end Runtime:addEventListener("unhandledError", unhandledErrorListener) local function networkListener( event )   a = a + nil end network.request("http://www.google.com", "GET", networkListener)

All network requests need an end point handler - this is a requirement of the framework.  I just have empty function and point all callbacks to it if I need no post-processing on the network request.  Or you could just do this

network.request("http://www.google.com", "GET", function() end)

Anyway, I confirm your findings.

Thank you for checking it out.  I have raised it as a bug through the bug reporting portal - however I am not sure if that is active.  Hopefully one of the main guys will see this thread and have a view…

Last time I submitted a bug report the portal had a bug (oh the irony).  Send an email and include a main.lua that shows the problem - you can use my code as that is the most simple case.

Send to support@coronalabs.com

Email sent.  Thanks a lot Adrian for your help.

The bug report didn’t have an attached project, but I was able to take the provided code and create a project for engineering to look at.

Rob

Thanks Rob. Not having raised an issue like this before, do you know if I will be told if the bug is fixed in a future build or should I just look out for it in the daily builds etc? Thanks again for passing it on. Keep up the good work - big fan of Corona!!

The task has this forum post link in it. Normally we announce it in the release notes for the daily builds. If the engineer can find the thread or if I can, we will try and update the thread as well. Ideally when the bug closes out, we will close out the support ticket which should trigger an email to you as well.

Rob

Unfortunately, the documentation on the unhandledError listener is somewhat opaque and needs to be improved:
 

These events occur when an application experiences a runtime error. They are only dispatched to the global Runtime object.

 
The second sentence describes what you are seeing since callbacks and listeners are effectively in their own runtime (for an internal CoronaSDK definition of “runtime”) but the consequences of that are not stated which are that errors in callbacks and listener code are not caught.
 
Having said that, it would be much better if the unhandledError listener worked more consistently and we will look into that.

I’ve also found that recently errors in network.request() callbacks don’t always log errors to the console either.

This is compounded if you then call another function from that callback as there is zero error handling available in any (nested) code called from the context of the callback.

I don’t remember this behaviour always being this way.

Thanks Perry for looking at this. I just want to underline the potential issue with the way the network.request currently works.  If your network.request call is to get some data you will inevitably want to do something with that data after it has returned.  The obvious thing to do is to put code in after the callback function has returned to process that data.  But, the way Corona currently works, ANY functions called after the callback has returned will not generate any error.  They will not crash the app.

If you imagine your network.request getting a lot of data.  If you have written lots of code to process that data on return - potentially configure screens based on that data - there is a lot of code that could go wrong, but the app will never fail and any error generated cannot be trapped.

I think this behavior is not ideal and could be very impactful for any apps which consume large amounts of data and dynamically configures screens based on that data returned…

When I tested the code that was posted in @adrianm’s post above, I got a run time error in my call back. The problem is that the code that tries to handle the exception isn’t handling the exception, correct?
 

Rob

Exactly Rob…

Here is an example to support my previous comment.  “ERROR” is not printed and, more worryingly, no error reported to the console for the divide by 0 error (for me with 3049 anyway)

function unhandledErrorListener( event )   print("ERROR") end Runtime:addEventListener("unhandledError", unhandledErrorListener) local function divideByZero(a)   return a / 0 end local function networkListener( event )   divideByZero(5) end network.request("http://www.google.com", "GET", networkListener)

No, I think it is a lot worse that that Rob. From my perspective the impact could be pretty serious. Yes, I agree it says the words Runtime Error in the console window and the execution of any code on that thread has stopped, but the app does not crash. I tested it on an iPad. There is nothing when the app runs to inform you that the code has generated a runtime error. In fact the app continues to receive interaction from the end user. My definition of a runtime error is that the program that generates it stops working. In this case, a runtime error is generated, but the programme continues, potentially in a imbalanced state. And there is no way of informing the end user that something wrong has happened as we can’t catch the error anyway. I would prefer it if the app crashed - in its current state, apps could be “crashing” all the time, but no one would ever know and functionality could be continuing, potentially with incomplete screens or with incomplete data. I’ll be honest. The more I have think about it, the more worried I am.