Version 12 of Errors management

Updated 2013-12-08 15:26:52 by PeterLewerin

Fabricio Rocha - 08-Feb-2010 - Error treatment in programming always seems to be an underestimated topic, often untold by and to newbies, while it's a useful thing that might be naturally taught along with the basics in a programming language. Only after some two years of studying Tcl/Tk I was able to find some information about this subject and develop myself a very basic and limited idea of how applications can avoid being crashed by bugs or misuse, so I would like to discuss some error management techniques with the experienced folks, while building up a tutorial from this discussion (something highly useful by aspiring Tclers like me). And, please, treat the errors you find...

Peter Lewerin - 2013-12-07 - I've overhauled the page a bit, concentrating the content around actual error management (leaving the in-depth description of the commands to the commands' respective pages) and adding the new throw/try commands.

Which error-management features are provided by Tcl?

Exception raising commands

These commands cause an error (raise an exception) that must be handled by code somewhere else in the program: if the error isn't handled (see below), the program stops.

return

The return command has been along for a long time, but has only been able to raise exceptions since Tcl 8.5.

The basic invocation is return result, where result is passed to the caller of the command that return was called in as the, well, result (return value) of that command.

When used to raise an exception, the minimal invocation is return -code error -level 0 result, and in this case result is treated as an error message to be handled (displayed) by exception handling code. Instead of -code error you may write -code 1: it has the same meaning.

Bug alert: The -level 0 option isn't required by documentation, but omitting it will in practice (in Tcl 8.6.1 at least) cause serious problems when the exception is to be handled (see Investigating exceptions when I get around to finishing that page (PL)).

Bug alert: using the -errorinfo option will mess with the -errorstack value in the return options dictionary, at least in Tcl 8.6.1 (see Investigating exceptions when I get around to finishing that page (PL)).

Exception handling code typically gets passed a dictionary, the return options dictionary, (see below) when an exception is raised. The -code and -level options to return are stored in that dictionary. Other options, such as -errorinfo, -errorcode, -errorline, and -errorstack, may be added to the invocation of return and have their values stored in the return options dictionary. The option -options, with a dictionary as value, may be used to set the return options dictionary all at once.

See return and the man page for a more thorough description of the command.

error

The error command has been the basic exception-raising command since Tcl 8.4.

It can be invoked in three ways:

The invocation......is functionally equivalent to:
"Unary error"error resultreturn -code error -level 0 result
"Binary error"error result inforeturn -code error -level 0 -errorinfo info result
"Ternary error"error result info codereturn -code error -level 0 -errorinfo info -errorcode code result

Bug alert: binary and ternary error will mess with the -errorstack value in the return options dictionary, at least in Tcl 8.6.1 (see Investigating exceptions when I get around to finishing that page (PL)).

See error and the man page for a more thorough description of the command.

throw

The throw command was added in Tcl 8.6.

It is invoked like this:

throw code result

which is functionally equivalent to:

return -code error -level 0 -errorcode code result

See throw and the man page for a more thorough description of the command.

Exception handling commands

An exception that isn't handled by the program is eventually handled by a default handler which basically only does two things: 1) output an error message, and 2) stop the program. If you want to do something else, like for instance deal with the error somehow and go on, or give up and at least save important data, you need to define an exception handler.

An exception handler has two parts: the grabber and the dispatcher (not the usual terms, but I'm trying to avoid verbs like catch and trap, which are used in the commands already). The grabber executes a script or body and intercepts any exceptions raised during the execution, capturing state information about the exception. This state information can then be used to dispatch to the exact piece of code that the programmer has assigned to deal with the exception in question.

catch

The catch command has been the basic exception-handling command since Tcl 8.4.

The catch command performs the grabbing part of exception handling, but does not dispatch. If fail is a command that raises an exception, this invocation:

catch { fail }

simply prevents the exception from stopping the program. The traditional invocation looks like this:

set returnCode [catch { cmd } result]

In this case, the variable result will either contain the return value of cmd, or the error message of an exception raised while executing cmd. This is disambiguated by the value in returnCode: if the value is 0 everything went well and result holds a return value. If an exception was raised, returnCode has the value 1.

(I'll be back soon -- PL)


unknown

Whenever a script invokes a command/procedure which is not defined anywhere, the Tcl interpreter triggers a built-in command called unknown. This command searches a definition of the procedure in other places than the interpreter's context, and if a procedure with the name is not found anywhere, unknown stops the script's processing and shows an error message in the console.

Like other Tcl built-in commands, ::unknown can be renamed and substituted by a procedure with the same name which can do other things before the default actions; and this is the way unknown is best used. Since Tcl 8.5, there is also the namespace unknown command, which allows the programmer to name a procedure which will be called when a command/procedure lookup fails in the scope of a specific namespace.

::errorCode

A reserved and global variable called errorCode is automatically created by the Tcl interpreter during the execution of a script for holding information about errors occurred in runtime, so its contents are changed everytime an error happens. errorCode is a variable-length list whose first element is a string which indicates the type of error which happened, and the following elements, if existant, are details about the errors which can be used by a procedure for error treatment. As of Tcl8.5, ::errorCode seems to be still underused by many of the core Tcl commands, and these are the possible values and structures that are generated by these commands and stored in ::errorCode, according to the official documentation [L1 ]:

  • "ARITH" code msg - Arithmetic error. The code element can contain the strings DIVZERO, DOMAIN, OVERFLOW or IOVERFLOW. msg contains a human-readable description of the problem.
  • "CHILDKILLED" pid sigName msg
  • "CHILDSUSP" pid sigName msg - Those errors are related to the use of processes in the underlying OS shell by the Tcl interpreter; more specifically, they contain information about processes which were unexpectedly terminated or suspended. pid is the process identifier; sigName is the signal which caused the process end or suspension; msg is a human-readable explanation of the problem. The list of possible values for sigName is in the system's C standard library signal.h header file (TODO: list them here).
  • "CHILDSTATUS" pid code - These values are set when an external program used by a Tcl script ends with non-zero value, which is considered an abnormal end. In such cases, the second element of ::errorCode will contain the process identifier number and the third one will hold the process "exit code". Actually, some system utilities intended for use in pipe sequences exit non-zero values as the correct result of their operations, so the code value may be the real and valid result of the child process.
  • "POSIX" errName msg - Lots of commands which depend on OS-provided functionalities, like file and socket operations, can result in errors of this family. The possible values for the errName item are listed in the errno.h header file of the C standard library (TODO: list them here). There is some contestation about the precision of these error reports, mainly under Windows, which is not exactly POSIX-compliant.
  • "NONE" - This single value in a one-element ::errorCode is set when a procedure generates an error -- intentionally or not -- but no detailed information is given about this error.

Any procedure can set its own error values in ::errorCode by using the "advanced" options for the command return, as we will see below.

catch

The catch command is the Tcl way for directly receiving the special values that return may have set in the case of an error. It runs a certain procedure/command under a second instance of the interpreter, which will not crash the application if something goes wrong. If the "catched" procedure ends abnormally -- i.e., its return -code is other than 0 --, catch will return exactly this code. Two variables names can optionally be passed to catch: the first one will receive the value passed by the called procedure's return command (which, in case of an error, is expected to be an explanation of the error) and the second one will hold a dict (which can be processed like a list of pairs) pretty similar to the contents of ::errorCode and to the extra options used in the return command, with the keys -errorcode, -errorinfo and -errorline.

error

The error command triggers the error-handling measures in the application and/or Tcl interpreter. Before Tcl 8.5 introduced some of the advanced options for the return command, error was the preferred way to intentionally signal an error.

bgerror

The bgerror command is called when an uncaught error reaches the Tcl/Tk event loop; it gives the application the ability to handle the error in some appropriate way. In GUI applications, it's common to report the error the user and give them the ability to easily send the stack trace and any related information to the developer. In non-GUI applications, it's useful to log the stack trace and related information to a log file; then, the application can either keep running or shutdown, as appropriate.

How to use all this stuff?

The infrastructure provided by Tcl allows applications to use exception handling, in the traditional sense of "try to do this, and if something goes wrong tell me and I'll see what can I do". This contrasts to the approach of "errors prediction", which, for example, performs a series of tests on the data which will be passed to a command for checking its validity, before the operation is performed. Both techniques are not excludent, however. Tcl allows various approaches to errors management, with their pros and cons:

Approach 1: return, catch and process the error

1) Always use the advanced return options when writing procedures which can cause or face errors, or which may give back an invalid result;

2) Always use catch for calling commands or your own procedures which can cause or face errors like described in 1;

3) Create a procedure to be called in the case that catch captures an error, for interpreting the error codes and, based on that, show error messages in friendly and standardized dialogs and perform operations which could minimize or solve the error.

Approach 2: tracing ::errorCode

Create a trace on ::errorCode, and a procedure to be called everytime it is modified, for interpreting the codes, display them, provide minimization measures, etc.

Any other? Please add what you do!

LV One useful thing that I sometimes use is creation of log files containing information intended to be useful in determining the state of the program during particular points. Sometimes, displaying information about the values of a number of variables is not as helpful as having that information written to a file - for instance, there are times when a GUI application might not have easy access to stderr for error traces. Writing information to a log file, which is available - and perhaps even emailable - to the programmer responsible is helpful.

Which errors shall be told to the user?

Failure in files, channels and sockets operations?

Errors caused by invalid inputs. It is often useful to use a distinct error code (e.g., INVALID) for data validation errors, as it makes it possible for the application to distinguish between errors in the user's input and errors in the validation or execution code.

Which errors shall NOT be told to the user?

Syntax errors and programming bugs - They'd better be fixed. Sure, but....

LV Certainly they need to be fixed. However, if you hide the info from the user, how will the programmer know what the bug/error is? Unless you have a guaranteed method of getting said info to the programmer (and email doesn't count - the user MIGHT be working off line), then providing the user with sufficent information to a) know what the error is and b) know who to contact or what to do about the problem seems the best approach to me.

Fabricio Rocha - 12-Feb-2010 - One more reason for having a way to intercept and explain this kind of errors to common users is that it seems that any test suite or any test routine will not be able to find some errors that users are able to find. Of course it is not nice to show weaknesses to a final user, but this is something practically unavoidable in software. And in addition to the situations listed by LV, we can consider that, for an open source/free software, providing good information about an error is a way to c) allow a user with sufficient programming knowledge to fix the problem and possibly contribute to the software development.

See Also: