Failure to catch an exception


#1

Hi!

I am getting into exception handling, but I am surprised at how my code works. Note that I am on Julia 0.6 (will port to 1.0 as soon as it’s out!). So, here goes:

function bungle(c)
    out = 0
    try
        @printf "Bungling away\n"
        out = 1
        if c == 1
            @assert false @sprintf "We have a main %s undervolt" "B"
        elseif c==2
            M = spzeros(1e4)
            v = randn(1e4)
            x = M\v
        elseif c==3
            a = sqrt(-1.)
        elseif c==4
            throw(DomainError())
        end    
    catch ex
        out = 2
        @printf "\nHouston we have a problem:\n%s\n" ex.msg
    finally
        @printf "Shutting down.\n"
        return out
    end
end # function bungle

@show bungle(0)

Calling bungle(0) and bungle(1) behaves as I intend. Bungle(2), bungle(3) and bungle(4) throw exceptions, which should be caught and reported. However, in these cases, the catch block is not executed, while the finaly block is. Why on earth!?!

:face_with_raised_eyebrow:

Philippe


#2

The reason is return statement in finally block, which does not allow an exception from try or catch block to propagate.

I am not a core dev, but I would say it is a bug, as the documentation says:

If the try block exits due to an exception, the exception will continue propagating

but we have the following behavior (I would say this is a MWE of the problem):

julia> function h()
           try
               sqrt(-1)
           finally
               return 1
           end
       end
h (generic function with 1 method)

julia> h()
1

and exception does not propagate. If this is intended then it is not documented.


#3

Thanks, you are right.

This has to be a bug. I cannot see how the combination of return statement and some exception types can make it logical not to execute the catch block.

I will plagiate your code and report this as a bug.

I will find some workaround: the reason I catch exceptions is I want to return whatever the computations have achieved (the steps that converged)…

Cheers

Philippe


#4

But in simpler MWE catch block is executed

julia> function h()
    try
        sqrt(-1)
    catch e
        println("Houston!")
    finally   return 1
    end
end;

julia> h()
Houston!
1

BTW your example prints error under 0.7.0-rc1.15

0.7.0-rc1.15> @show bungle(3)
Bungling away

Houston we have a problem:
sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
Shutting down.
bungle(3) = 2
2

#5

In all examples catch block is executed. The problem is that when an exception in catch block is thrown then it is not passed through if there is a return statement in finally.

And the specific problem is in the original code that not all exceptions have msg field. Then the Julia throws error that is not passed through later. But the core problem is what I have explained in my MWE (without catch). If in this MWE you move out return past finally error is raised:

julia> function h()
           try
               sqrt(-1)
           finally
               println("Finally")
           end
           return 1
       end
h (generic function with 1 method)
julia> h()
Finally
ERROR: DomainError:
Stacktrace:
 [1] h() at .\REPL[4]:3

#6

Thanks for adding this explanation! :slight_smile:

In Julia 0.6.1 I got this:

julia> @show bungle(3)
Bungling away
Shutting down.
bungle(3) = 2
2

compare it to what I wrote before about 0.7.0. I didn’t analyze it deeply but it seemed to me that in 0.6.1 printf to Houston was not processed. (I could be wrong).


#7

The reason is that DomainError has field msg in Julia 0.7 and it does not have it in Julia 0.6. So in Julia 0.7 no error is thrown in catch clause and all is processed cleanly.


#8

That’s it, you got it: I bugged my catch! Julia goes free.

Thank you, now this will be easy to correct.

: )

Philippe


#9

Maybe core-devs can comment here before we file an issue?

The question is whether return statement in finally clause should suppress propagation of an exception thrown in:

  • catch block
  • try block when there is no catch block

CC @jeff.bezanson


#10

Issues are best handled on Github.
It has the right tools for the job,
In terms of cross-referencing; devs getting notifications,
closing, etc etc.

Just open an issue.
It may be closed, it may not.
But trust me no one will get mad for creating an issue prematurely without discussing it with them on discourse first.