[erlang-bugs] handling of uncaught exceptions in behavour callbacks

Richard Carlsson <>
Wed Apr 25 10:43:55 CEST 2012

When gen_server, gen_fsm, etc., make a call to one of the behaviour 
callback functions, these calls are protected by a construct on the 
following form (simplified):

     case catch Mod:some_callback(...) of
         {'EXIT',Reason} -> exit(Reason);
         Other -> exit({bad_return_value, Other})

When a callback terminates with an exception of type 'error' or 'exit', 
this sort of does the expected thing (although it converts error 
exceptions to exit exceptions, which might or might not be intended). 
But if the callback terminates due to an uncaught throw(Term), it is 
treated as if the callback had returned Term. At best, this is an 
undocumented and horrible way of letting you write callback functions 
that can do things like throw({reply, Reply, NewState}) for nonlocal 
return out of a deep recursion and back to the gen_server code. But I'd 
like to think that it's simply unintended behaviour. (Nothing I could 
see in the OTP documentation describes what the gen_server is supposed 
to do with exceptions thrown from a callback.)

The annoyance this causes is that if you use throw(X) for anything 
within the callback (or in library code called by the callback) and you 
don't make sure to wrap your callback in something that catches all 
throws, any such uncaught exception will result in the gen_server 
process terminating with the confusing reason {bad_return_value, X} (and 
no stack trace to indicate where X was thrown from).

If it can be agreed that this is a bug that should be fixed (and how), 
we could submit a patch.

     /Richard Carlsson and Samuel Rivas

More information about the erlang-bugs mailing list