Run-Time Errors and Exception Handling

ASCOM's Application Programming Interfaces (APIs) incorporate the fundamental programming principle of "Do it right or raise an exception". If you need to refresh your understanding of this principle and its consequences, please use the preceding link then come back here to understand the details of exceptions within the ASCOM COM and ASCOM Alpaca environment.

Modern Exception Handling

All modern languages and run time environments use "out of band" exception reporting and handling [*]. By out of band is meant that upon an error condition being detected or reported (an "exceptional" condition), the normal program execution flow is interrupted, and execution resumes somewhere else in some sort of pre-arranged error handling code. This goes back as far as the original C language which has setjmp/longjmp ("hack the stack and go back Jack!") which is an out of band exception system. Unix "signals" are another example.

Virtually all popular languages use Structured Exception Handling. Most commonly this consists of a special statement like throw or raise which carries packaged information like a line number, textual error message, and a numeric error code, or even an encapsulated "inner exception". Exception handling code is pre-arranged via try{}catch(e){} or the syntactic equivalent, with 'e' being the exception info package/object. It's beyond the scope of this paper to provide more detail as this technique is universal.

In modular systems where a program may be using a 3rd party package, library, or a separate process, or in a system like Python or Node.js where packages are widely used, it's often essential that the name of the source of the exception be part of the packaged info. Furthermore in some languages, it is possible for the exception package to contain an entire embedded exception package ("inner exception") that may originate in a downstream or sub-module.

For example, your code makes a call to a local library and that library makes a call to an endpoint on another system. On the remote device an error occurs and the local library receives an error response. The library function you called has now failed and must raise an exception. The exception source would be the library you called, and the details might include a message that a call to a particular endpoint (one of several possibilities) failed and the details should describe the consequences of that failure to the library. That exception package could (should) include an inner exception whose source is the lower-level API itself, and message indicating what went wrong over on the device's server, and ideally why.

Finally some programming environments (e.g. C# and Java) support multiple types of exceptions and separate multiple individual catch(exception-type ex) error handling blocks for each type of exception.

Exceptions in ASCOM Alpaca and COM

Applications must be prepared to catch exceptions from any Alpaca endpoint or ASCOM COM property or method, including Completion Properties for (asynchronous) ASCOM methods. This should go without saying since anyone who writes software in a modern language and ecosystem needs to be prepared for run time errors and exceptions. Unfortunately, writing software to control physical devices (mounts, domes, etc) needs to be prepared for exceptions because those physical devices (and ther connections like USB) are much more susceptible to error states compared to pure code.

All error conditions are reported by raising exceptions, and you may get exceptions from any property or method, so be prepared!

This includes both Alpaca and COM. Both method calls and completion properties may raise exceptions. In COM the exception is a true run-time error. WIth Alpaca, the exception comes back as a JSON object with non-zero ErrorNumber and non-null ErrorMessage. A simple exception in Alpaca is this one coming from a mount that cannot slew when sidereal tracking is false.

{
   "ClientTransactionID":4531,
   "ServerTransactionID":4527,
   "ErrorNumber":1035,
   "ErrorMessage":""SlewToCoordinatesAsync is not allowed when tracking is False"
}

Exceptions aren't always this simple. Be prepared for an Alpaca response like this. This is the same exception but from the ASCOM COM Telescope Simulator.NET, converted into an Alpaca exception by ASCOM Remote. This is an extreme example. The point here is that you need to be prepared for anything that is a legal exception. Note that if you wish, you can ignore the DriverException field of the Alpaca JSON response object if you don't think your users would care. In any case the key is that there is a non-zero ErrorNumber, and a non-null Error Message. Be sure to report the primary error to your user!

{
   "ClientTransactionID":26243,
   "ServerTransactionID":310,
   "ErrorNumber":1035,
   "ErrorMessage":"SlewToCoordinatesAsync is not allowed when tracking is False",
   "DriverException":{
      "ClassName":"System.Runtime.InteropServices.COMException",
      "Message":"SlewToCoordinatesAsync is not allowed when tracking is False",
      "Data":null,
      "InnerException":null,
      "HelpURL":null,
      "StackTraceString":"   at System.Dynamic.ComRuntimeHelpers.CheckThrowException(Int32 hresult, ExcepInfo& excepInfo, UInt32 argErr, String message)\r\n   at CallSite.Target(Closure , CallSite , ComObject , Double , Double )\r\n   at CallSite.Target(Closure , CallSite , Object , Double , Double )\r\n   at ASCOM.Remote.ServerForm.CallMethod(String deviceType, RequestData requestData) in J:\\ASCOMRemote\\Remote Server\\ServerForm.cs:line 5792",
      "RemoteStackTraceString":null,
      "RemoteStackIndex":0,
      "ExceptionMethod":"8\nCheckThrowException\nSystem.Dynamic, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\nSystem.Dynamic.ComRuntimeHelpers\nVoid CheckThrowException(Int32, System.Dynamic.ExcepInfo ByRef, UInt32, System.String)",
      "HResult":-2147220469,
      "Source":"ASCOM.Simulator.Telescope",
      "WatsonBuckets":null
   }
}

If you get something like this from an Alpaca end point, you should consider this just like an out-of-band run time error raised by the Alpaca-speaking device.


[*] It is no longer acceptable to use "sentinel" return values like -1 or a Boolean to indicate if an error occurred within the called logic. The disadvantages of this to app developers are beyond belief.