haskellexceptionscotty

Why a pattern-matching failure is not catching by the exception handler?


Why pattern-matching failure is not catching by my exception handler excToStr in this case?

I have a handler of an incoming POST request as under the control of the Scotty Web framework:

...
import qualified Web.Scotty as W
...

    W.post "/some/endpoint" $ excToStr "Cannot handle it!" $ do
        b <- W.body

        -- throwString "aaa" <--- THIS IS HANDLED FINE!

        Just x::Maybe SomeMyType <- pure (decode b) -- BUT NOT THIS PATTERN-MATCHING FAILURE!
        liftIO $ print $ show x
        W.text "OK"

where excToStr is mine and it looks like:

...
import qualified Data.Text.Lazy as LT
...

excH :: (String -> String) -> ActionT LT.Text IO () -> ActionT LT.Text IO ()
excH mkErr m = catchAnyDeep m (W.text . cs . mkErr . show)

excToStr :: String -> ActionT LT.Text IO () -> ActionT LT.Text IO ()
excToStr errMsg = excH (\details -> errMsg <> " (" <> details <> ")")

catchAnyDeep is from safe-exceptions library. I tried another functions as well (catchAny, handle, catch, etc) - without success. The crux of the problem is that when incoming body cannot be decoded successfully (and decode returns Nothing instead of Just x), then pattern-match fails, so I expect that my extToStr (ie. excH) will handle it because catchAnyDeep (and catchAny) handles ANY exception (including pattern-match failures, right?):

catchAny :: MonadCatch m => m a -> (SomeException -> m a) -> m a

and

catchAnyDeep :: (MonadCatch m, MonadIO m, NFData a) => m a -> (SomeException -> m a) -> m a.

If I throw just an exception with throwString then it works as expected (the exception gets caught). But the pattern matching failure leads to an HTTP internal error 500 with a message "Pattern match failure in do expression at....". How to handle pattern-matching exceptions as well?


Solution

  • There are two forms of exceptions in a scotty action (of type ActionT Text IO). There are native exceptions in IO, and another form added by the ActionT transformer. These exceptions are handled separately. The interface is given by the instances of ActionT: