Anything in the Exception class can be thrown, and can be wrapped with the SomeException constructor:
data SomeException = forall e . Exception e => SomeException e
The Exception class has methods toException and fromException to convert your exception type e to and from SomeException respectively.
class (Typeable e, Show e) => Exception e where toException :: e -> SomeException fromException :: SomeException -> Maybe e
The fromException method is just cast by default, which returns Nothing when applied to the wrong type.
So there's no sure way of knowing at a particular point in the code whether some exception has been thrown inside that is now causing its execution to be abandoned, unless you have just tried to catch that exact exception type. This suggests that creating a lot of knew Exception instances with other Exception values wrapped inside is not a good strategy. Instead we catch the IOException, wrap it up in another type e, and then return it in a function with a MonadError e m constraint.