try Query.Delete; // - an exception is raised here except Query.Transaction.Rollback; ShowMessage('Sorry, could not delete the report'); Exit; end;What's happening? Does EurekaLog really ignore user code?
If the client code were written like this:
try Query.Delete; // - an exception is raised here except Exit; end;then there would be no problem:
Query.Delete
raises an exception, the exception is caught by the except
block, which does nothing, and execution continues further, EurekaLog does not react (*). However, the client code looks different: its
except
block does some (complex) exception recovery.In any case, such exceptions are called handled because they are handled by the user's (client's) code. A handled exception is handled by cleanup code, which performs some recovery/rollback actions, after which program execution continues and the exception itself is removed.
In general, any cleanup code (be it a destructor or a rollback/recovery code) should be written in a such way so it will not raise exceptions. It seems to me that the logic here is obvious: if your normal code raises an exception, then you can always suggest a possible recovery actions (the so-called “Plan B”). For example, if an exception occurs when opening a document - then the document needs to be closed; If an exception occurs while accessing the printer - the print job must be canceled. If there is an error opening a file - you can try to open it again after a pause or user's action. And so on.
But what will you do if an error occurs in the “Plan B” itself? You wanted to cancel the print job, but it... is not cancelled. So what now? You will not be able to offer any reasonable recovery actions. Because you don't know what happened. Your variables may be corrupted. Hell, you might not even be able to allocate a block of memory right now! The only possible clean way is to kill/restart the application.
This is why the cleanup code must NOT raise exceptions. Because you can't do anything meaningful with these exceptions. In other words: the cleanup code must handle all exceptions that it knows how to handle.
But in practice, things may not be the same as in an ideal world.
In this case, the client's code raises a second exception inside
Query.Transaction.Rollback
. It means that the execution of the except
block is interrupted and execution moves to the exception handler higher up the stack. It turns out that there are no more try
blocks higher up the stack in the client's code, so execution is transferred to the global exception handler, which calls EurekaLog to generate a bug report.Correcting the code will involve following the rule “cleanup code must not raise exceptions” (“cleanup code must handle known exceptions”). For example, as follows:
try Query.Delete; // - an exception is raised here except try Query.Transaction.Rollback; except on EDatabaseError do; // - ignore database errors end; ShowMessage('Sorry, could not delete the report'); Exit; end;In this case, the second exception will be handled in place and will not "bubble up" to the next exception handler (be it a
try
block or a global exception handler). It is important to write the correct code - i.e. so that it doesn't block exceptions that you don't know how to handle. For example, the following code is incorrect:try Query.Transaction.Rollback; except // - ignore ALL errors end;It is incorrect because you do not know how to properly handle/rollback/recover from, for example, an
EAccessViolation
exception. It is best to write the code so that it takes into account the most narrow conditions for exceptions - only those that you know exactly how to handle. For example, if you know a specific type/code of the error when canceling a transaction - then write like this:try Query.Transaction.Rollback; except on E: ESomeSpecificDatabaseException do if E.ErrorCode = 1234 then begin // - ignore only this specific error end else raise; end;
Remarks:
- (*) ...unless the Catch Handled Exceptions option is not enabled in EurekaLog. When the Catch Handled Exceptions option is enabled - EurekaLog will show bug reports about all exceptions, even if they were handled by
except
blocks. This option is intended for local debugging, as an aid in finding a "bad" code. It should not be used in production. - If you don't need to do special cleanup after an exception, but need to block an expected exception, then you can ignore it, or mark it as "expected".
- If you do not fix your code so that your recovery/handling code does not raise exceptions, then you will have secondary exceptions. EurekaLog has a set of options that control EurekaLog's behavior for such exceptions.