diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,18 @@
 [The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-server/CHANGELOG.md)
 [Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md)
 
+0.16
+----
+
+- Rename `ServantError` to `ClientError`, `ServantErr` to `ServerError`
+  [#1131](https://github.com/haskell-servant/pull/1131)
+- *servant-server* Reorder HTTP failure code priorities
+  [#1103](https://github.com/haskell-servant/servant/pull/1103)
+- *servant-server* Re-organise internal modules
+  [#1139](https://github.com/haskell-servant/servant/pull/1139)
+- Allow `network-3.0`
+  [#1107](https://github.com/haskell-servant/pull/1107)
+
 0.15
 ----
 
diff --git a/servant-server.cabal b/servant-server.cabal
--- a/servant-server.cabal
+++ b/servant-server.cabal
@@ -1,6 +1,6 @@
 cabal-version:       >=1.10
 name:                servant-server
-version:             0.15
+version:             0.16
 
 synopsis:            A family of combinators for defining webservices APIs and serving them
 category:            Servant, Web
@@ -21,13 +21,13 @@
 license-file:        LICENSE
 author:              Servant Contributors
 maintainer:          haskell-servant-maintainers@googlegroups.com
-copyright:           2014-2016 Zalora South East Asia Pte Ltd, 2016-2018 Servant Contributors
+copyright:           2014-2016 Zalora South East Asia Pte Ltd, 2016-2019 Servant Contributors
 build-type:          Custom
 tested-with:
   GHC ==8.0.2
    || ==8.2.2
    || ==8.4.4
-   || ==8.6.2
+   || ==8.6.3
 
 extra-source-files:
   CHANGELOG.md
@@ -52,10 +52,13 @@
     Servant.Server.Internal
     Servant.Server.Internal.BasicAuth
     Servant.Server.Internal.Context
+    Servant.Server.Internal.Delayed
+    Servant.Server.Internal.DelayedIO
     Servant.Server.Internal.Handler
     Servant.Server.Internal.Router
+    Servant.Server.Internal.RouteResult
     Servant.Server.Internal.RoutingApplication
-    Servant.Server.Internal.ServantErr
+    Servant.Server.Internal.ServerError
     Servant.Server.StaticFiles
 
   -- deprecated
@@ -76,7 +79,7 @@
   -- Servant dependencies
   -- strict dependency as we re-export 'servant' things.
   build-depends:
-      servant             >= 0.15     && < 0.15.1
+      servant             >= 0.16     && < 0.17.1
     , http-api-data       >= 0.4      && < 0.4.1
 
   -- Other dependencies: Lower bound around what is in the latest Stackage LTS.
@@ -87,9 +90,9 @@
     , exceptions          >= 0.10.0   && < 0.11
     , http-media          >= 0.7.1.3  && < 0.8
     , http-types          >= 0.12.2   && < 0.13
-    , network-uri         >= 2.6.1.0  && < 2.7
+    , network-uri         >= 2.6.1.0  && < 2.8
     , monad-control       >= 1.0.2.3  && < 1.1
-    , network             >= 2.8      && < 2.9
+    , network             >= 2.8      && < 3.1
     , string-conversions  >= 0.4.0.1  && < 0.5
     , resourcet           >= 1.2.2    && < 1.3
     , tagged              >= 0.8.6    && < 0.9
@@ -160,7 +163,7 @@
   build-depends:
       aeson                >= 1.4.1.0  && < 1.5
     , directory            >= 1.3.0.0  && < 1.4
-    , hspec                >= 2.6.0    && < 2.7
+    , hspec                >= 2.6.0    && < 2.8
     , hspec-wai            >= 0.9.0    && < 0.10
     , QuickCheck           >= 2.12.6.1 && < 2.13
     , should-not-typecheck >= 2.1.0    && < 2.2
@@ -168,7 +171,7 @@
     , wai-extra            >= 3.0.24.3 && < 3.1
 
   build-tool-depends:
-    hspec-discover:hspec-discover >= 2.6.0 && <2.7
+    hspec-discover:hspec-discover >= 2.6.0 && <2.8
 
 test-suite doctests
   build-depends:
diff --git a/src/Servant/Server.hs b/src/Servant/Server.hs
--- a/src/Servant/Server.hs
+++ b/src/Servant/Server.hs
@@ -49,7 +49,7 @@
   -- , mkAuthHandler
 
     -- * Default error type
-  , ServantErr(..)
+  , ServerError(..)
     -- ** 3XX
   , err300
   , err301
diff --git a/src/Servant/Server/Experimental/Auth.hs b/src/Servant/Server/Experimental/Auth.hs
--- a/src/Servant/Server/Experimental/Auth.hs
+++ b/src/Servant/Server/Experimental/Auth.hs
@@ -27,11 +27,9 @@
                  ((:>))
 import           Servant.API.Experimental.Auth
 import           Servant.Server.Internal
-                 (HasContextEntry, HasServer (..), getContextEntry)
-import           Servant.Server.Internal.Handler
-                 (Handler, runHandler)
-import           Servant.Server.Internal.RoutingApplication
-                 (DelayedIO, addAuthCheck, delayedFailFatal, withRequest)
+                 (DelayedIO, Handler, HasContextEntry, HasServer (..),
+                 addAuthCheck, delayedFailFatal, getContextEntry, runHandler,
+                 withRequest)
 
 -- * General Auth
 
diff --git a/src/Servant/Server/Internal.hs b/src/Servant/Server/Internal.hs
--- a/src/Servant/Server/Internal.hs
+++ b/src/Servant/Server/Internal.hs
@@ -22,10 +22,13 @@
   ( module Servant.Server.Internal
   , module Servant.Server.Internal.BasicAuth
   , module Servant.Server.Internal.Context
+  , module Servant.Server.Internal.Delayed
+  , module Servant.Server.Internal.DelayedIO
   , module Servant.Server.Internal.Handler
   , module Servant.Server.Internal.Router
+  , module Servant.Server.Internal.RouteResult
   , module Servant.Server.Internal.RoutingApplication
-  , module Servant.Server.Internal.ServantErr
+  , module Servant.Server.Internal.ServerError
   ) where
 
 import           Control.Monad
@@ -88,10 +91,13 @@
 
 import           Servant.Server.Internal.BasicAuth
 import           Servant.Server.Internal.Context
+import           Servant.Server.Internal.Delayed
+import           Servant.Server.Internal.DelayedIO
 import           Servant.Server.Internal.Handler
 import           Servant.Server.Internal.Router
+import           Servant.Server.Internal.RouteResult
 import           Servant.Server.Internal.RoutingApplication
-import           Servant.Server.Internal.ServantErr
+import           Servant.Server.Internal.ServerError
 
 #ifdef HAS_TYPE_ERROR
 import           GHC.TypeLits
diff --git a/src/Servant/Server/Internal/BasicAuth.hs b/src/Servant/Server/Internal/BasicAuth.hs
--- a/src/Servant/Server/Internal/BasicAuth.hs
+++ b/src/Servant/Server/Internal/BasicAuth.hs
@@ -9,7 +9,7 @@
                  (guard)
 import           Control.Monad.Trans
                  (liftIO)
-import qualified Data.ByteString                            as BS
+import qualified Data.ByteString                     as BS
 import           Data.ByteString.Base64
                  (decodeLenient)
 import           Data.Monoid
@@ -26,8 +26,8 @@
 
 import           Servant.API.BasicAuth
                  (BasicAuthData (BasicAuthData))
-import           Servant.Server.Internal.RoutingApplication
-import           Servant.Server.Internal.ServantErr
+import           Servant.Server.Internal.DelayedIO
+import           Servant.Server.Internal.ServerError
 
 -- * Basic Auth
 
diff --git a/src/Servant/Server/Internal/Delayed.hs b/src/Servant/Server/Internal/Delayed.hs
new file mode 100644
--- /dev/null
+++ b/src/Servant/Server/Internal/Delayed.hs
@@ -0,0 +1,272 @@
+{-# LANGUAGE CPP                   #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE GADTs                 #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE RecordWildCards       #-}
+{-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE TypeOperators         #-}
+{-# LANGUAGE UndecidableInstances  #-}
+module Servant.Server.Internal.Delayed where
+
+import           Control.Monad.IO.Class
+                 (MonadIO (..))
+import           Control.Monad.Reader
+                 (ask)
+import           Control.Monad.Trans.Resource
+                 (ResourceT, runResourceT)
+import           Network.Wai
+                 (Request, Response)
+
+import           Servant.Server.Internal.DelayedIO
+import           Servant.Server.Internal.Handler
+import           Servant.Server.Internal.RouteResult
+import           Servant.Server.Internal.ServerError
+
+-- | A 'Delayed' is a representation of a handler with scheduled
+-- delayed checks that can trigger errors.
+--
+-- Why would we want to delay checks?
+--
+-- There are two reasons:
+--
+-- 1. In a straight-forward implementation, the order in which we
+-- perform checks will determine the error we generate. This is
+-- because once an error occurs, we would abort and not perform
+-- any subsequent checks, but rather return the current error.
+--
+-- This is not a necessity: we could continue doing other checks,
+-- and choose the preferred error. However, that would in general
+-- mean more checking, which leads us to the other reason.
+--
+-- 2. We really want to avoid doing certain checks too early. For
+-- example, captures involve parsing, and are much more costly
+-- than static route matches. In particular, if several paths
+-- contain the "same" capture, we'd like as much as possible to
+-- avoid trying the same parse many times. Also tricky is the
+-- request body. Again, this involves parsing, but also, WAI makes
+-- obtaining the request body a side-effecting operation. We
+-- could/can work around this by manually caching the request body,
+-- but we'd rather keep the number of times we actually try to
+-- decode the request body to an absolute minimum.
+--
+-- We prefer to have the following relative priorities of error
+-- codes:
+--
+-- @
+-- 404
+-- 405 (bad method)
+-- 401 (unauthorized)
+-- 415 (unsupported media type)
+-- 406 (not acceptable)
+-- 400 (bad request)
+-- @
+--
+-- Therefore, while routing, we delay most checks so that they
+-- will ultimately occur in the right order.
+--
+-- A 'Delayed' contains many delayed blocks of tests, and
+-- the actual handler:
+--
+-- 1. Delayed captures. These can actually cause 404, and
+-- while they're costly, they should be done first among the
+-- delayed checks (at least as long as we do not decouple the
+-- check order from the error reporting, see above). Delayed
+-- captures can provide inputs to the actual handler.
+--
+-- 2. Method check(s). This can cause a 405. On success,
+-- it does not provide an input for the handler. Method checks
+-- are comparatively cheap.
+--
+-- 3. Authentication checks. This can cause 401.
+--
+-- 4. Accept and content type header checks. These checks
+-- can cause 415 and 406 errors.
+--
+-- 5. Query parameter checks. They require parsing and can cause 400 if the
+-- parsing fails. Query parameter checks provide inputs to the handler
+--
+-- 6. Header Checks. They also require parsing and can cause 400 if parsing fails.
+--
+-- 7. Body check. The request body check can cause 400.
+--
+data Delayed env c where
+  Delayed :: { capturesD :: env -> DelayedIO captures
+             , methodD   :: DelayedIO ()
+             , authD     :: DelayedIO auth
+             , acceptD   :: DelayedIO ()
+             , contentD  :: DelayedIO contentType
+             , paramsD   :: DelayedIO params
+             , headersD  :: DelayedIO headers
+             , bodyD     :: contentType -> DelayedIO body
+             , serverD   :: captures
+                         -> params
+                         -> headers
+                         -> auth
+                         -> body
+                         -> Request
+                         -> RouteResult c
+             } -> Delayed env c
+
+instance Functor (Delayed env) where
+  fmap f Delayed{..} =
+    Delayed
+      { serverD = \ c p h a b req -> f <$> serverD c p h a b req
+      , ..
+      } -- Note [Existential Record Update]
+
+-- | A 'Delayed' without any stored checks.
+emptyDelayed :: RouteResult a -> Delayed env a
+emptyDelayed result =
+  Delayed (const r) r r r r r r (const r) (\ _ _ _ _ _ _ -> result)
+  where
+    r = return ()
+
+-- | Add a capture to the end of the capture block.
+addCapture :: Delayed env (a -> b)
+           -> (captured -> DelayedIO a)
+           -> Delayed (captured, env) b
+addCapture Delayed{..} new =
+  Delayed
+    { capturesD = \ (txt, env) -> (,) <$> capturesD env <*> new txt
+    , serverD   = \ (x, v) p h a b req -> ($ v) <$> serverD x p h a b req
+    , ..
+    } -- Note [Existential Record Update]
+
+-- | Add a parameter check to the end of the params block
+addParameterCheck :: Delayed env (a -> b)
+                  -> DelayedIO a
+                  -> Delayed env b
+addParameterCheck Delayed {..} new =
+  Delayed
+    { paramsD = (,) <$> paramsD <*> new
+    , serverD = \c (p, pNew) h a b req -> ($ pNew) <$> serverD c p h a b req
+    , ..
+    }
+
+-- | Add a parameter check to the end of the params block
+addHeaderCheck :: Delayed env (a -> b)
+               -> DelayedIO a
+               -> Delayed env b
+addHeaderCheck Delayed {..} new =
+  Delayed
+    { headersD = (,) <$> headersD <*> new
+    , serverD = \c p (h, hNew) a b req -> ($ hNew) <$> serverD c p h a b req
+    , ..
+    }
+
+-- | Add a method check to the end of the method block.
+addMethodCheck :: Delayed env a
+               -> DelayedIO ()
+               -> Delayed env a
+addMethodCheck Delayed{..} new =
+  Delayed
+    { methodD = methodD <* new
+    , ..
+    } -- Note [Existential Record Update]
+
+-- | Add an auth check to the end of the auth block.
+addAuthCheck :: Delayed env (a -> b)
+             -> DelayedIO a
+             -> Delayed env b
+addAuthCheck Delayed{..} new =
+  Delayed
+    { authD   = (,) <$> authD <*> new
+    , serverD = \ c p h (y, v) b req -> ($ v) <$> serverD c p h y b req
+    , ..
+    } -- Note [Existential Record Update]
+
+-- | Add a content type and body checks around parameter checks.
+--
+-- We'll report failed content type check (415), before trying to parse
+-- query parameters (400). Which, in turn, happens before request body parsing.
+addBodyCheck :: Delayed env (a -> b)
+             -> DelayedIO c         -- ^ content type check
+             -> (c -> DelayedIO a)  -- ^ body check
+             -> Delayed env b
+addBodyCheck Delayed{..} newContentD newBodyD =
+  Delayed
+    { contentD = (,) <$> contentD <*> newContentD
+    , bodyD    = \(content, c) -> (,) <$> bodyD content <*> newBodyD c
+    , serverD  = \ c p h a (z, v) req -> ($ v) <$> serverD c p h a z req
+    , ..
+    } -- Note [Existential Record Update]
+
+
+-- | Add an accept header check before handling parameters.
+-- In principle, we'd like
+-- to take a bad body (400) response take precedence over a
+-- failed accept check (406). BUT to allow streaming the body,
+-- we cannot run the body check and then still backtrack.
+-- We therefore do the accept check before the body check,
+-- when we can still backtrack. There are other solutions to
+-- this, but they'd be more complicated (such as delaying the
+-- body check further so that it can still be run in a situation
+-- where we'd otherwise report 406).
+addAcceptCheck :: Delayed env a
+               -> DelayedIO ()
+               -> Delayed env a
+addAcceptCheck Delayed{..} new =
+  Delayed
+    { acceptD = acceptD *> new
+    , ..
+    } -- Note [Existential Record Update]
+
+-- | Many combinators extract information that is passed to
+-- the handler without the possibility of failure. In such a
+-- case, 'passToServer' can be used.
+passToServer :: Delayed env (a -> b) -> (Request -> a) -> Delayed env b
+passToServer Delayed{..} x =
+  Delayed
+    { serverD = \ c p h a b req -> ($ x req) <$> serverD c p h a b req
+    , ..
+    } -- Note [Existential Record Update]
+
+-- | Run a delayed server. Performs all scheduled operations
+-- in order, and passes the results from the capture and body
+-- blocks on to the actual handler.
+--
+-- This should only be called once per request; otherwise the guarantees about
+-- effect and HTTP error ordering break down.
+runDelayed :: Delayed env a
+           -> env
+           -> Request
+           -> ResourceT IO (RouteResult a)
+runDelayed Delayed{..} env = runDelayedIO $ do
+    r <- ask
+    c <- capturesD env
+    methodD
+    a <- authD
+    acceptD
+    content <- contentD
+    p <- paramsD       -- Has to be before body parsing, but after content-type checks
+    h <- headersD
+    b <- bodyD content
+    liftRouteResult (serverD c p h a b r)
+
+-- | Runs a delayed server and the resulting action.
+-- Takes a continuation that lets us send a response.
+-- Also takes a continuation for how to turn the
+-- result of the delayed server into a response.
+runAction :: Delayed env (Handler a)
+          -> env
+          -> Request
+          -> (RouteResult Response -> IO r)
+          -> (a -> RouteResult Response)
+          -> IO r
+runAction action env req respond k = runResourceT $
+    runDelayed action env req >>= go >>= liftIO . respond
+  where
+    go (Fail e)      = return $ Fail e
+    go (FailFatal e) = return $ FailFatal e
+    go (Route a)     = liftIO $ do
+      e <- runHandler a
+      case e of
+        Left err -> return . Route $ responseServerError err
+        Right x  -> return $! k x
+
+{- Note [Existential Record Update]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Due to GHC issue <https://ghc.haskell.org/trac/ghc/ticket/2595 2595>, we cannot
+do the more succint thing - just update the records we actually change.
+-}
diff --git a/src/Servant/Server/Internal/DelayedIO.hs b/src/Servant/Server/Internal/DelayedIO.hs
new file mode 100644
--- /dev/null
+++ b/src/Servant/Server/Internal/DelayedIO.hs
@@ -0,0 +1,72 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE TypeFamilies               #-}
+module Servant.Server.Internal.DelayedIO where
+
+import           Control.Monad.Base
+                 (MonadBase (..))
+import           Control.Monad.Catch
+                 (MonadThrow (..))
+import           Control.Monad.Reader
+                 (MonadReader (..), ReaderT (..), runReaderT)
+import           Control.Monad.Trans
+                 (MonadIO (..), MonadTrans (..))
+import           Control.Monad.Trans.Control
+                 (ComposeSt, MonadBaseControl (..), MonadTransControl (..),
+                 defaultLiftBaseWith, defaultRestoreM)
+import           Control.Monad.Trans.Resource
+                 (MonadResource (..), ResourceT, runInternalState,
+                 runResourceT, transResourceT, withInternalState)
+import           Network.Wai
+                 (Application, Request, Response, ResponseReceived)
+
+import           Servant.Server.Internal.RouteResult
+import           Servant.Server.Internal.ServerError
+
+-- | Computations used in a 'Delayed' can depend on the
+-- incoming 'Request', may perform 'IO', and result in a
+-- 'RouteResult', meaning they can either suceed, fail
+-- (with the possibility to recover), or fail fatally.
+--
+newtype DelayedIO a = DelayedIO { runDelayedIO' :: ReaderT Request (ResourceT (RouteResultT IO)) a }
+  deriving
+    ( Functor, Applicative, Monad
+    , MonadIO, MonadReader Request
+    , MonadThrow
+    , MonadResource
+    )
+
+instance MonadBase IO DelayedIO where
+    liftBase = liftIO
+
+liftRouteResult :: RouteResult a -> DelayedIO a
+liftRouteResult x = DelayedIO $ lift . lift $ RouteResultT . return $ x
+
+instance MonadBaseControl IO DelayedIO where
+    -- type StM DelayedIO a = StM (ReaderT Request (ResourceT (RouteResultT IO))) a
+    -- liftBaseWith f = DelayedIO $ liftBaseWith $ \g -> f (g . runDelayedIO')
+    -- restoreM       = DelayedIO . restoreM
+
+    type StM DelayedIO a = RouteResult a
+    liftBaseWith f = DelayedIO $ ReaderT $ \req -> withInternalState $ \s ->
+        liftBaseWith $ \runInBase -> f $ \x ->
+            runInBase (runInternalState (runReaderT (runDelayedIO' x) req) s)
+    restoreM      = DelayedIO . lift . withInternalState . const . restoreM
+
+
+runDelayedIO :: DelayedIO a -> Request -> ResourceT IO (RouteResult a)
+runDelayedIO m req = transResourceT runRouteResultT $ runReaderT (runDelayedIO' m) req
+
+-- | Fail with the option to recover.
+delayedFail :: ServerError -> DelayedIO a
+delayedFail err = liftRouteResult $ Fail err
+
+-- | Fail fatally, i.e., without any option to recover.
+delayedFailFatal :: ServerError -> DelayedIO a
+delayedFailFatal err = liftRouteResult $ FailFatal err
+
+-- | Gain access to the incoming request.
+withRequest :: (Request -> DelayedIO a) -> DelayedIO a
+withRequest f = do
+    req <- ask
+    f req
diff --git a/src/Servant/Server/Internal/Handler.hs b/src/Servant/Server/Internal/Handler.hs
--- a/src/Servant/Server/Internal/Handler.hs
+++ b/src/Servant/Server/Internal/Handler.hs
@@ -22,13 +22,13 @@
                  (ExceptT, runExceptT)
 import           GHC.Generics
                  (Generic)
-import           Servant.Server.Internal.ServantErr
-                 (ServantErr)
+import           Servant.Server.Internal.ServerError
+                 (ServerError)
 
-newtype Handler a = Handler { runHandler' :: ExceptT ServantErr IO a }
+newtype Handler a = Handler { runHandler' :: ExceptT ServerError IO a }
   deriving
     ( Functor, Applicative, Monad, MonadIO, Generic
-    , MonadError ServantErr
+    , MonadError ServerError
     , MonadThrow, MonadCatch, MonadMask
     )
 
@@ -36,7 +36,7 @@
   liftBase = Handler . liftBase
 
 instance MonadBaseControl IO Handler where
-  type StM Handler a = Either ServantErr a
+  type StM Handler a = Either ServerError a
 
   -- liftBaseWith :: (RunInBase Handler IO -> IO a) -> Handler a
   liftBaseWith f = Handler (liftBaseWith (\g -> f (g . runHandler')))
@@ -44,5 +44,5 @@
   -- restoreM :: StM Handler a -> Handler a
   restoreM st = Handler (restoreM st)
 
-runHandler :: Handler a -> IO (Either ServantErr a)
+runHandler :: Handler a -> IO (Either ServerError a)
 runHandler = runExceptT . runHandler'
diff --git a/src/Servant/Server/Internal/RouteResult.hs b/src/Servant/Server/Internal/RouteResult.hs
new file mode 100644
--- /dev/null
+++ b/src/Servant/Server/Internal/RouteResult.hs
@@ -0,0 +1,76 @@
+{-# LANGUAGE DeriveFunctor         #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE UndecidableInstances  #-}
+module Servant.Server.Internal.RouteResult where
+
+import           Control.Monad
+                 (ap, liftM)
+import           Control.Monad.Base
+                 (MonadBase (..))
+import           Control.Monad.Catch
+                 (MonadThrow (..))
+import           Control.Monad.Trans
+                 (MonadIO (..), MonadTrans (..))
+import           Control.Monad.Trans.Control
+                 (ComposeSt, MonadBaseControl (..), MonadTransControl (..),
+                 defaultLiftBaseWith, defaultRestoreM)
+
+import           Servant.Server.Internal.ServerError
+
+-- | The result of matching against a path in the route tree.
+data RouteResult a =
+    Fail ServerError           -- ^ Keep trying other paths.
+                               --   The 'ServantError' should only be 404, 405 or 406.
+  | FailFatal !ServerError     -- ^ Don't try other paths.
+  | Route !a
+  deriving (Eq, Show, Read, Functor)
+
+instance Applicative RouteResult where
+    pure = return
+    (<*>) = ap
+
+instance Monad RouteResult where
+    return = Route
+    Route a     >>= f = f a
+    Fail e      >>= _ = Fail e
+    FailFatal e >>= _ = FailFatal e
+
+newtype RouteResultT m a = RouteResultT { runRouteResultT :: m (RouteResult a) }
+  deriving (Functor)
+
+instance MonadTrans RouteResultT where
+    lift = RouteResultT . liftM Route
+
+instance (Functor m, Monad m) => Applicative (RouteResultT m) where
+    pure  = return
+    (<*>) = ap
+
+instance Monad m => Monad (RouteResultT m) where
+    return = RouteResultT . return . Route
+    m >>= k = RouteResultT $ do
+        a <- runRouteResultT m
+        case a of
+            Fail e      -> return $ Fail e
+            FailFatal e -> return $ FailFatal e
+            Route b     -> runRouteResultT (k b)
+
+instance MonadIO m => MonadIO (RouteResultT m) where
+    liftIO = lift . liftIO
+
+instance MonadBase b m => MonadBase b (RouteResultT m) where
+    liftBase = lift . liftBase
+
+instance MonadBaseControl b m => MonadBaseControl b (RouteResultT m) where
+    type StM (RouteResultT m) a = ComposeSt RouteResultT m a
+    liftBaseWith = defaultLiftBaseWith
+    restoreM     = defaultRestoreM
+
+instance MonadTransControl RouteResultT where
+    type StT RouteResultT a = RouteResult a
+    liftWith f = RouteResultT $ liftM return $ f runRouteResultT
+    restoreT = RouteResultT
+
+instance MonadThrow m => MonadThrow (RouteResultT m) where
+    throwM = lift . throwM
diff --git a/src/Servant/Server/Internal/Router.hs b/src/Servant/Server/Internal/Router.hs
--- a/src/Servant/Server/Internal/Router.hs
+++ b/src/Servant/Server/Internal/Router.hs
@@ -7,6 +7,8 @@
 import           Prelude ()
 import           Prelude.Compat
 
+import           Data.Function
+                 (on)
 import           Data.Map
                  (Map)
 import qualified Data.Map                                   as M
@@ -16,7 +18,8 @@
 import           Network.Wai
                  (Response, pathInfo)
 import           Servant.Server.Internal.RoutingApplication
-import           Servant.Server.Internal.ServantErr
+import           Servant.Server.Internal.RouteResult
+import           Servant.Server.Internal.ServerError
 
 type Router env = Router' env RoutingApplication
 
@@ -208,7 +211,14 @@
 
 -- Priority on HTTP codes.
 --
--- It just so happens that 404 < 405 < 406 as far as
--- we are concerned here, so we can use (<).
 worseHTTPCode :: Int -> Int -> Bool
-worseHTTPCode = (<)
+worseHTTPCode = on (<) toPriority
+  where
+    toPriority :: Int -> Int
+    toPriority 404 = 0 -- not found
+    toPriority 405 = 1 -- method not allowed
+    toPriority 401 = 2 -- unauthorized
+    toPriority 415 = 3 -- unsupported media type
+    toPriority 406 = 4 -- not acceptable
+    toPriority 400 = 6 -- bad request
+    toPriority _   = 5
diff --git a/src/Servant/Server/Internal/RoutingApplication.hs b/src/Servant/Server/Internal/RoutingApplication.hs
--- a/src/Servant/Server/Internal/RoutingApplication.hs
+++ b/src/Servant/Server/Internal/RoutingApplication.hs
@@ -1,399 +1,20 @@
-{-# LANGUAGE CPP                        #-}
-{-# LANGUAGE DeriveFunctor              #-}
-{-# LANGUAGE FlexibleInstances          #-}
-{-# LANGUAGE GADTs                      #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE MultiParamTypeClasses      #-}
-{-# LANGUAGE RecordWildCards            #-}
-{-# LANGUAGE TypeFamilies               #-}
-{-# LANGUAGE TypeOperators              #-}
-{-# LANGUAGE UndecidableInstances       #-}
 module Servant.Server.Internal.RoutingApplication where
 
-import           Control.Monad
-                 (ap, liftM)
-import           Control.Monad.Base
-                 (MonadBase (..))
-import           Control.Monad.Catch
-                 (MonadThrow (..))
-import           Control.Monad.Reader
-                 (MonadReader (..), ReaderT (..), runReaderT)
-import           Control.Monad.Trans
-                 (MonadIO (..), MonadTrans (..))
-import           Control.Monad.Trans.Control
-                 (ComposeSt, MonadBaseControl (..), MonadTransControl (..),
-                 defaultLiftBaseWith, defaultRestoreM)
-import           Control.Monad.Trans.Resource
-                 (MonadResource (..), ResourceT, runInternalState,
-                 runResourceT, transResourceT, withInternalState)
 import           Network.Wai
                  (Application, Request, Response, ResponseReceived)
 import           Prelude ()
 import           Prelude.Compat
-import           Servant.Server.Internal.Handler
-import           Servant.Server.Internal.ServantErr
+import           Servant.Server.Internal.RouteResult
+import           Servant.Server.Internal.ServerError
 
 type RoutingApplication =
      Request -- ^ the request, the field 'pathInfo' may be modified by url routing
   -> (RouteResult Response -> IO ResponseReceived) -> IO ResponseReceived
 
--- | The result of matching against a path in the route tree.
-data RouteResult a =
-    Fail ServantErr           -- ^ Keep trying other paths. The @ServantErr@
-                              -- should only be 404, 405 or 406.
-  | FailFatal !ServantErr     -- ^ Don't try other paths.
-  | Route !a
-  deriving (Eq, Show, Read, Functor)
-
-instance Applicative RouteResult where
-    pure = return
-    (<*>) = ap
-
-instance Monad RouteResult where
-    return = Route
-    Route a     >>= f = f a
-    Fail e      >>= _ = Fail e
-    FailFatal e >>= _ = FailFatal e
-
-newtype RouteResultT m a = RouteResultT { runRouteResultT :: m (RouteResult a) }
-  deriving (Functor)
-
-instance MonadTrans RouteResultT where
-    lift = RouteResultT . liftM Route
-
-instance (Functor m, Monad m) => Applicative (RouteResultT m) where
-    pure  = return
-    (<*>) = ap
-
-instance Monad m => Monad (RouteResultT m) where
-    return = RouteResultT . return . Route
-    m >>= k = RouteResultT $ do
-        a <- runRouteResultT m
-        case a of
-            Fail e      -> return $ Fail e
-            FailFatal e -> return $ FailFatal e
-            Route b     -> runRouteResultT (k b)
-
-instance MonadIO m => MonadIO (RouteResultT m) where
-    liftIO = lift . liftIO
-
-instance MonadBase b m => MonadBase b (RouteResultT m) where
-    liftBase = lift . liftBase
-
-instance MonadBaseControl b m => MonadBaseControl b (RouteResultT m) where
-    type StM (RouteResultT m) a = ComposeSt RouteResultT m a
-    liftBaseWith = defaultLiftBaseWith
-    restoreM     = defaultRestoreM
-
-instance MonadTransControl RouteResultT where
-    type StT RouteResultT a = RouteResult a
-    liftWith f = RouteResultT $ liftM return $ f runRouteResultT
-    restoreT = RouteResultT
-
-instance MonadThrow m => MonadThrow (RouteResultT m) where
-    throwM = lift . throwM
-
 toApplication :: RoutingApplication -> Application
 toApplication ra request respond = ra request routingRespond
  where
   routingRespond :: RouteResult Response -> IO ResponseReceived
-  routingRespond (Fail err)      = respond $ responseServantErr err
-  routingRespond (FailFatal err) = respond $ responseServantErr err
+  routingRespond (Fail err)      = respond $ responseServerError err
+  routingRespond (FailFatal err) = respond $ responseServerError err
   routingRespond (Route v)       = respond v
-
--- | A 'Delayed' is a representation of a handler with scheduled
--- delayed checks that can trigger errors.
---
--- Why would we want to delay checks?
---
--- There are two reasons:
---
--- 1. In a straight-forward implementation, the order in which we
--- perform checks will determine the error we generate. This is
--- because once an error occurs, we would abort and not perform
--- any subsequent checks, but rather return the current error.
---
--- This is not a necessity: we could continue doing other checks,
--- and choose the preferred error. However, that would in general
--- mean more checking, which leads us to the other reason.
---
--- 2. We really want to avoid doing certain checks too early. For
--- example, captures involve parsing, and are much more costly
--- than static route matches. In particular, if several paths
--- contain the "same" capture, we'd like as much as possible to
--- avoid trying the same parse many times. Also tricky is the
--- request body. Again, this involves parsing, but also, WAI makes
--- obtaining the request body a side-effecting operation. We
--- could/can work around this by manually caching the request body,
--- but we'd rather keep the number of times we actually try to
--- decode the request body to an absolute minimum.
---
--- We prefer to have the following relative priorities of error
--- codes:
---
--- @
--- 404
--- 405 (bad method)
--- 401 (unauthorized)
--- 415 (unsupported media type)
--- 406 (not acceptable)
--- 400 (bad request)
--- @
---
--- Therefore, while routing, we delay most checks so that they
--- will ultimately occur in the right order.
---
--- A 'Delayed' contains many delayed blocks of tests, and
--- the actual handler:
---
--- 1. Delayed captures. These can actually cause 404, and
--- while they're costly, they should be done first among the
--- delayed checks (at least as long as we do not decouple the
--- check order from the error reporting, see above). Delayed
--- captures can provide inputs to the actual handler.
---
--- 2. Method check(s). This can cause a 405. On success,
--- it does not provide an input for the handler. Method checks
--- are comparatively cheap.
---
--- 3. Authentication checks. This can cause 401.
---
--- 4. Accept and content type header checks. These checks
--- can cause 415 and 406 errors.
---
--- 5. Query parameter checks. They require parsing and can cause 400 if the
--- parsing fails. Query parameter checks provide inputs to the handler
---
--- 6. Header Checks. They also require parsing and can cause 400 if parsing fails.
---
--- 7. Body check. The request body check can cause 400.
---
-data Delayed env c where
-  Delayed :: { capturesD :: env -> DelayedIO captures
-             , methodD   :: DelayedIO ()
-             , authD     :: DelayedIO auth
-             , acceptD   :: DelayedIO ()
-             , contentD  :: DelayedIO contentType
-             , paramsD   :: DelayedIO params
-             , headersD  :: DelayedIO headers
-             , bodyD     :: contentType -> DelayedIO body
-             , serverD   :: captures
-                         -> params
-                         -> headers
-                         -> auth
-                         -> body
-                         -> Request
-                         -> RouteResult c
-             } -> Delayed env c
-
-instance Functor (Delayed env) where
-  fmap f Delayed{..} =
-    Delayed
-      { serverD = \ c p h a b req -> f <$> serverD c p h a b req
-      , ..
-      } -- Note [Existential Record Update]
-
--- | Computations used in a 'Delayed' can depend on the
--- incoming 'Request', may perform 'IO, and result in a
--- 'RouteResult, meaning they can either suceed, fail
--- (with the possibility to recover), or fail fatally.
---
-newtype DelayedIO a = DelayedIO { runDelayedIO' :: ReaderT Request (ResourceT (RouteResultT IO)) a }
-  deriving
-    ( Functor, Applicative, Monad
-    , MonadIO, MonadReader Request
-    , MonadThrow
-    , MonadResource
-    )
-
-instance MonadBase IO DelayedIO where
-    liftBase = liftIO
-
-liftRouteResult :: RouteResult a -> DelayedIO a
-liftRouteResult x = DelayedIO $ lift . lift $ RouteResultT . return $ x
-
-instance MonadBaseControl IO DelayedIO where
-    -- type StM DelayedIO a = StM (ReaderT Request (ResourceT (RouteResultT IO))) a
-    -- liftBaseWith f = DelayedIO $ liftBaseWith $ \g -> f (g . runDelayedIO')
-    -- restoreM       = DelayedIO . restoreM
-
-    type StM DelayedIO a = RouteResult a
-    liftBaseWith f = DelayedIO $ ReaderT $ \req -> withInternalState $ \s ->
-        liftBaseWith $ \runInBase -> f $ \x ->
-            runInBase (runInternalState (runReaderT (runDelayedIO' x) req) s)
-    restoreM      = DelayedIO . lift . withInternalState . const . restoreM
-
-
-runDelayedIO :: DelayedIO a -> Request -> ResourceT IO (RouteResult a)
-runDelayedIO m req = transResourceT runRouteResultT $ runReaderT (runDelayedIO' m) req
-
--- | A 'Delayed' without any stored checks.
-emptyDelayed :: RouteResult a -> Delayed env a
-emptyDelayed result =
-  Delayed (const r) r r r r r r (const r) (\ _ _ _ _ _ _ -> result)
-  where
-    r = return ()
-
--- | Fail with the option to recover.
-delayedFail :: ServantErr -> DelayedIO a
-delayedFail err = liftRouteResult $ Fail err
-
--- | Fail fatally, i.e., without any option to recover.
-delayedFailFatal :: ServantErr -> DelayedIO a
-delayedFailFatal err = liftRouteResult $ FailFatal err
-
--- | Gain access to the incoming request.
-withRequest :: (Request -> DelayedIO a) -> DelayedIO a
-withRequest f = do
-    req <- ask
-    f req
-
--- | Add a capture to the end of the capture block.
-addCapture :: Delayed env (a -> b)
-           -> (captured -> DelayedIO a)
-           -> Delayed (captured, env) b
-addCapture Delayed{..} new =
-  Delayed
-    { capturesD = \ (txt, env) -> (,) <$> capturesD env <*> new txt
-    , serverD   = \ (x, v) p h a b req -> ($ v) <$> serverD x p h a b req
-    , ..
-    } -- Note [Existential Record Update]
-
--- | Add a parameter check to the end of the params block
-addParameterCheck :: Delayed env (a -> b)
-                  -> DelayedIO a
-                  -> Delayed env b
-addParameterCheck Delayed {..} new =
-  Delayed
-    { paramsD = (,) <$> paramsD <*> new
-    , serverD = \c (p, pNew) h a b req -> ($ pNew) <$> serverD c p h a b req
-    , ..
-    }
-
--- | Add a parameter check to the end of the params block
-addHeaderCheck :: Delayed env (a -> b)
-               -> DelayedIO a
-               -> Delayed env b
-addHeaderCheck Delayed {..} new =
-  Delayed
-    { headersD = (,) <$> headersD <*> new
-    , serverD = \c p (h, hNew) a b req -> ($ hNew) <$> serverD c p h a b req
-    , ..
-    }
-
--- | Add a method check to the end of the method block.
-addMethodCheck :: Delayed env a
-               -> DelayedIO ()
-               -> Delayed env a
-addMethodCheck Delayed{..} new =
-  Delayed
-    { methodD = methodD <* new
-    , ..
-    } -- Note [Existential Record Update]
-
--- | Add an auth check to the end of the auth block.
-addAuthCheck :: Delayed env (a -> b)
-             -> DelayedIO a
-             -> Delayed env b
-addAuthCheck Delayed{..} new =
-  Delayed
-    { authD   = (,) <$> authD <*> new
-    , serverD = \ c p h (y, v) b req -> ($ v) <$> serverD c p h y b req
-    , ..
-    } -- Note [Existential Record Update]
-
--- | Add a content type and body checks around parameter checks.
---
--- We'll report failed content type check (415), before trying to parse
--- query parameters (400). Which, in turn, happens before request body parsing.
-addBodyCheck :: Delayed env (a -> b)
-             -> DelayedIO c         -- ^ content type check
-             -> (c -> DelayedIO a)  -- ^ body check
-             -> Delayed env b
-addBodyCheck Delayed{..} newContentD newBodyD =
-  Delayed
-    { contentD = (,) <$> contentD <*> newContentD
-    , bodyD    = \(content, c) -> (,) <$> bodyD content <*> newBodyD c
-    , serverD  = \ c p h a (z, v) req -> ($ v) <$> serverD c p h a z req
-    , ..
-    } -- Note [Existential Record Update]
-
-
--- | Add an accept header check before handling parameters.
--- In principle, we'd like
--- to take a bad body (400) response take precedence over a
--- failed accept check (406). BUT to allow streaming the body,
--- we cannot run the body check and then still backtrack.
--- We therefore do the accept check before the body check,
--- when we can still backtrack. There are other solutions to
--- this, but they'd be more complicated (such as delaying the
--- body check further so that it can still be run in a situation
--- where we'd otherwise report 406).
-addAcceptCheck :: Delayed env a
-               -> DelayedIO ()
-               -> Delayed env a
-addAcceptCheck Delayed{..} new =
-  Delayed
-    { acceptD = acceptD *> new
-    , ..
-    } -- Note [Existential Record Update]
-
--- | Many combinators extract information that is passed to
--- the handler without the possibility of failure. In such a
--- case, 'passToServer' can be used.
-passToServer :: Delayed env (a -> b) -> (Request -> a) -> Delayed env b
-passToServer Delayed{..} x =
-  Delayed
-    { serverD = \ c p h a b req -> ($ x req) <$> serverD c p h a b req
-    , ..
-    } -- Note [Existential Record Update]
-
--- | Run a delayed server. Performs all scheduled operations
--- in order, and passes the results from the capture and body
--- blocks on to the actual handler.
---
--- This should only be called once per request; otherwise the guarantees about
--- effect and HTTP error ordering break down.
-runDelayed :: Delayed env a
-           -> env
-           -> Request
-           -> ResourceT IO (RouteResult a)
-runDelayed Delayed{..} env = runDelayedIO $ do
-    r <- ask
-    c <- capturesD env
-    methodD
-    a <- authD
-    acceptD
-    content <- contentD
-    p <- paramsD       -- Has to be before body parsing, but after content-type checks
-    h <- headersD
-    b <- bodyD content
-    liftRouteResult (serverD c p h a b r)
-
--- | Runs a delayed server and the resulting action.
--- Takes a continuation that lets us send a response.
--- Also takes a continuation for how to turn the
--- result of the delayed server into a response.
-runAction :: Delayed env (Handler a)
-          -> env
-          -> Request
-          -> (RouteResult Response -> IO r)
-          -> (a -> RouteResult Response)
-          -> IO r
-runAction action env req respond k = runResourceT $
-    runDelayed action env req >>= go >>= liftIO . respond
-  where
-    go (Fail e)      = return $ Fail e
-    go (FailFatal e) = return $ FailFatal e
-    go (Route a)     = liftIO $ do
-      e <- runHandler a
-      case e of
-        Left err -> return . Route $ responseServantErr err
-        Right x  -> return $! k x
-
-{- Note [Existential Record Update]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Due to GHC issue <https://ghc.haskell.org/trac/ghc/ticket/2595 2595>, we cannot
-do the more succint thing - just update the records we actually change.
--}
diff --git a/src/Servant/Server/Internal/ServantErr.hs b/src/Servant/Server/Internal/ServantErr.hs
deleted file mode 100644
--- a/src/Servant/Server/Internal/ServantErr.hs
+++ /dev/null
@@ -1,475 +0,0 @@
-{-# LANGUAGE DeriveDataTypeable #-}
-{-# LANGUAGE OverloadedStrings  #-}
-{-# LANGUAGE RecordWildCards    #-}
-module Servant.Server.Internal.ServantErr where
-
-import           Control.Exception
-                 (Exception)
-import qualified Data.ByteString.Char8 as BS
-import qualified Data.ByteString.Lazy  as LBS
-import           Data.Typeable
-                 (Typeable)
-import qualified Network.HTTP.Types    as HTTP
-import           Network.Wai
-                 (Response, responseLBS)
-
-data ServantErr = ServantErr { errHTTPCode     :: Int
-                             , errReasonPhrase :: String
-                             , errBody         :: LBS.ByteString
-                             , errHeaders      :: [HTTP.Header]
-                             } deriving (Show, Eq, Read, Typeable)
-
-instance Exception ServantErr
-
-responseServantErr :: ServantErr -> Response
-responseServantErr ServantErr{..} = responseLBS status errHeaders errBody
-  where
-    status = HTTP.mkStatus errHTTPCode (BS.pack errReasonPhrase)
-
--- | 'err300' Multiple Choices
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err300 { errBody = "I can't choose." }
---
-err300 :: ServantErr
-err300 = ServantErr { errHTTPCode = 300
-                    , errReasonPhrase = "Multiple Choices"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err301' Moved Permanently
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError err301
---
-err301 :: ServantErr
-err301 = ServantErr { errHTTPCode = 301
-                    , errReasonPhrase = "Moved Permanently"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err302' Found
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError err302
---
-err302 :: ServantErr
-err302 = ServantErr { errHTTPCode = 302
-                    , errReasonPhrase = "Found"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err303' See Other
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError err303
---
-err303 :: ServantErr
-err303 = ServantErr { errHTTPCode = 303
-                    , errReasonPhrase = "See Other"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err304' Not Modified
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError err304
---
-err304 :: ServantErr
-err304 = ServantErr { errHTTPCode = 304
-                    , errReasonPhrase = "Not Modified"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err305' Use Proxy
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError err305
---
-err305 :: ServantErr
-err305 = ServantErr { errHTTPCode = 305
-                    , errReasonPhrase = "Use Proxy"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err307' Temporary Redirect
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError err307
---
-err307 :: ServantErr
-err307 = ServantErr { errHTTPCode = 307
-                    , errReasonPhrase = "Temporary Redirect"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err400' Bad Request
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err400 { errBody = "Your request makes no sense to me." }
---
-err400 :: ServantErr
-err400 = ServantErr { errHTTPCode = 400
-                    , errReasonPhrase = "Bad Request"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err401' Unauthorized
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err401 { errBody = "Your credentials are invalid." }
---
-err401 :: ServantErr
-err401 = ServantErr { errHTTPCode = 401
-                    , errReasonPhrase = "Unauthorized"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err402' Payment Required
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err402 { errBody = "You have 0 credits. Please give me $$$." }
---
-err402 :: ServantErr
-err402 = ServantErr { errHTTPCode = 402
-                    , errReasonPhrase = "Payment Required"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err403' Forbidden
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err403 { errBody = "Please login first." }
---
-err403 :: ServantErr
-err403 = ServantErr { errHTTPCode = 403
-                    , errReasonPhrase = "Forbidden"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err404' Not Found
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err404 { errBody = "(╯°□°）╯︵ ┻━┻)." }
---
-err404 :: ServantErr
-err404 = ServantErr { errHTTPCode = 404
-                    , errReasonPhrase = "Not Found"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err405' Method Not Allowed
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err405 { errBody = "Your account privileges does not allow for this.  Please pay $$$." }
---
-err405 :: ServantErr
-err405 = ServantErr { errHTTPCode = 405
-                    , errReasonPhrase = "Method Not Allowed"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err406' Not Acceptable
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError err406
---
-err406 :: ServantErr
-err406 = ServantErr { errHTTPCode = 406
-                    , errReasonPhrase = "Not Acceptable"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err407' Proxy Authentication Required
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError err407
---
-err407 :: ServantErr
-err407 = ServantErr { errHTTPCode = 407
-                    , errReasonPhrase = "Proxy Authentication Required"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err409' Conflict
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err409 { errBody = "Transaction conflicts with 59879cb56c7c159231eeacdd503d755f7e835f74" }
---
-err409 :: ServantErr
-err409 = ServantErr { errHTTPCode = 409
-                    , errReasonPhrase = "Conflict"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err410' Gone
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err410 { errBody = "I know it was here at some point, but.. I blame bad luck." }
---
-err410 :: ServantErr
-err410 = ServantErr { errHTTPCode = 410
-                    , errReasonPhrase = "Gone"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err411' Length Required
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError err411
---
-err411 :: ServantErr
-err411 = ServantErr { errHTTPCode = 411
-                    , errReasonPhrase = "Length Required"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err412' Precondition Failed
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err412 { errBody = "Precondition fail: x < 42 && y > 57" }
---
-err412 :: ServantErr
-err412 = ServantErr { errHTTPCode = 412
-                    , errReasonPhrase = "Precondition Failed"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err413' Request Entity Too Large
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err413 { errBody = "Request exceeded 64k." }
---
-err413 :: ServantErr
-err413 = ServantErr { errHTTPCode = 413
-                    , errReasonPhrase = "Request Entity Too Large"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err414' Request-URI Too Large
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err414 { errBody = "Maximum length is 64." }
---
-err414 :: ServantErr
-err414 = ServantErr { errHTTPCode = 414
-                    , errReasonPhrase = "Request-URI Too Large"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err415' Unsupported Media Type
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err415 { errBody = "Supported media types:  gif, png" }
---
-err415 :: ServantErr
-err415 = ServantErr { errHTTPCode = 415
-                    , errReasonPhrase = "Unsupported Media Type"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err416' Request range not satisfiable
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err416 { errBody = "Valid range is [0, 424242]." }
---
-err416 :: ServantErr
-err416 = ServantErr { errHTTPCode = 416
-                    , errReasonPhrase = "Request range not satisfiable"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err417' Expectation Failed
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err417 { errBody = "I found a quux in the request.  This isn't going to work." }
---
-err417 :: ServantErr
-err417 = ServantErr { errHTTPCode = 417
-                    , errReasonPhrase = "Expectation Failed"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err418' Expectation Failed
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err418 { errBody = "Apologies, this is not a webserver but a teapot." }
---
-err418 :: ServantErr
-err418 = ServantErr { errHTTPCode = 418
-                    , errReasonPhrase = "I'm a teapot"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err422' Unprocessable Entity
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err422 { errBody = "I understood your request, but can't process it." }
---
-err422 :: ServantErr
-err422 = ServantErr { errHTTPCode = 422
-                    , errReasonPhrase = "Unprocessable Entity"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err500' Internal Server Error
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err500 { errBody = "Exception in module A.B.C:55.  Have a great day!" }
---
-err500 :: ServantErr
-err500 = ServantErr { errHTTPCode = 500
-                    , errReasonPhrase = "Internal Server Error"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err501' Not Implemented
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err501 { errBody = "/v1/foo is not supported with quux in the request." }
---
-err501 :: ServantErr
-err501 = ServantErr { errHTTPCode = 501
-                    , errReasonPhrase = "Not Implemented"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err502' Bad Gateway
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err502 { errBody = "Tried gateway foo, bar, and baz.  None responded." }
---
-err502 :: ServantErr
-err502 = ServantErr { errHTTPCode = 502
-                    , errReasonPhrase = "Bad Gateway"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err503' Service Unavailable
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err503 { errBody = "We're rewriting in PHP." }
---
-err503 :: ServantErr
-err503 = ServantErr { errHTTPCode = 503
-                    , errReasonPhrase = "Service Unavailable"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err504' Gateway Time-out
---
--- Example:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err504 { errBody = "Backend foobar did not respond in 5 seconds." }
---
-err504 :: ServantErr
-err504 = ServantErr { errHTTPCode = 504
-                    , errReasonPhrase = "Gateway Time-out"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
-
--- | 'err505' HTTP Version not supported
---
--- Example usage:
---
--- > failingHandler :: Handler ()
--- > failingHandler = throwError $ err505 { errBody = "I support HTTP/4.0 only." }
---
-err505 :: ServantErr
-err505 = ServantErr { errHTTPCode = 505
-                    , errReasonPhrase = "HTTP Version not supported"
-                    , errBody = ""
-                    , errHeaders = []
-                    }
diff --git a/src/Servant/Server/Internal/ServerError.hs b/src/Servant/Server/Internal/ServerError.hs
new file mode 100644
--- /dev/null
+++ b/src/Servant/Server/Internal/ServerError.hs
@@ -0,0 +1,477 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE OverloadedStrings  #-}
+{-# LANGUAGE RecordWildCards    #-}
+module Servant.Server.Internal.ServerError where
+
+import           Control.Exception
+                 (Exception)
+import qualified Data.ByteString.Char8 as BS
+import qualified Data.ByteString.Lazy  as LBS
+import           Data.Typeable
+                 (Typeable)
+import qualified Network.HTTP.Types    as HTTP
+import           Network.Wai
+                 (Response, responseLBS)
+
+data ServerError = ServerError
+    { errHTTPCode     :: Int
+    , errReasonPhrase :: String
+    , errBody         :: LBS.ByteString
+    , errHeaders      :: [HTTP.Header]
+    }
+  deriving (Show, Eq, Read, Typeable)
+
+instance Exception ServerError
+
+responseServerError :: ServerError -> Response
+responseServerError ServerError{..} = responseLBS status errHeaders errBody
+  where
+    status = HTTP.mkStatus errHTTPCode (BS.pack errReasonPhrase)
+
+-- | 'err300' Multiple Choices
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err300 { errBody = "I can't choose." }
+--
+err300 :: ServerError
+err300 = ServerError { errHTTPCode = 300
+                    , errReasonPhrase = "Multiple Choices"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err301' Moved Permanently
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError err301
+--
+err301 :: ServerError
+err301 = ServerError { errHTTPCode = 301
+                    , errReasonPhrase = "Moved Permanently"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err302' Found
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError err302
+--
+err302 :: ServerError
+err302 = ServerError { errHTTPCode = 302
+                    , errReasonPhrase = "Found"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err303' See Other
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError err303
+--
+err303 :: ServerError
+err303 = ServerError { errHTTPCode = 303
+                    , errReasonPhrase = "See Other"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err304' Not Modified
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError err304
+--
+err304 :: ServerError
+err304 = ServerError { errHTTPCode = 304
+                    , errReasonPhrase = "Not Modified"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err305' Use Proxy
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError err305
+--
+err305 :: ServerError
+err305 = ServerError { errHTTPCode = 305
+                    , errReasonPhrase = "Use Proxy"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err307' Temporary Redirect
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError err307
+--
+err307 :: ServerError
+err307 = ServerError { errHTTPCode = 307
+                    , errReasonPhrase = "Temporary Redirect"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err400' Bad Request
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err400 { errBody = "Your request makes no sense to me." }
+--
+err400 :: ServerError
+err400 = ServerError { errHTTPCode = 400
+                    , errReasonPhrase = "Bad Request"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err401' Unauthorized
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err401 { errBody = "Your credentials are invalid." }
+--
+err401 :: ServerError
+err401 = ServerError { errHTTPCode = 401
+                    , errReasonPhrase = "Unauthorized"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err402' Payment Required
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err402 { errBody = "You have 0 credits. Please give me $$$." }
+--
+err402 :: ServerError
+err402 = ServerError { errHTTPCode = 402
+                    , errReasonPhrase = "Payment Required"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err403' Forbidden
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err403 { errBody = "Please login first." }
+--
+err403 :: ServerError
+err403 = ServerError { errHTTPCode = 403
+                    , errReasonPhrase = "Forbidden"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err404' Not Found
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err404 { errBody = "(╯°□°）╯︵ ┻━┻)." }
+--
+err404 :: ServerError
+err404 = ServerError { errHTTPCode = 404
+                    , errReasonPhrase = "Not Found"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err405' Method Not Allowed
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err405 { errBody = "Your account privileges does not allow for this.  Please pay $$$." }
+--
+err405 :: ServerError
+err405 = ServerError { errHTTPCode = 405
+                    , errReasonPhrase = "Method Not Allowed"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err406' Not Acceptable
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError err406
+--
+err406 :: ServerError
+err406 = ServerError { errHTTPCode = 406
+                    , errReasonPhrase = "Not Acceptable"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err407' Proxy Authentication Required
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError err407
+--
+err407 :: ServerError
+err407 = ServerError { errHTTPCode = 407
+                    , errReasonPhrase = "Proxy Authentication Required"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err409' Conflict
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err409 { errBody = "Transaction conflicts with 59879cb56c7c159231eeacdd503d755f7e835f74" }
+--
+err409 :: ServerError
+err409 = ServerError { errHTTPCode = 409
+                    , errReasonPhrase = "Conflict"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err410' Gone
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err410 { errBody = "I know it was here at some point, but.. I blame bad luck." }
+--
+err410 :: ServerError
+err410 = ServerError { errHTTPCode = 410
+                    , errReasonPhrase = "Gone"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err411' Length Required
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError err411
+--
+err411 :: ServerError
+err411 = ServerError { errHTTPCode = 411
+                    , errReasonPhrase = "Length Required"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err412' Precondition Failed
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err412 { errBody = "Precondition fail: x < 42 && y > 57" }
+--
+err412 :: ServerError
+err412 = ServerError { errHTTPCode = 412
+                    , errReasonPhrase = "Precondition Failed"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err413' Request Entity Too Large
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err413 { errBody = "Request exceeded 64k." }
+--
+err413 :: ServerError
+err413 = ServerError { errHTTPCode = 413
+                    , errReasonPhrase = "Request Entity Too Large"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err414' Request-URI Too Large
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err414 { errBody = "Maximum length is 64." }
+--
+err414 :: ServerError
+err414 = ServerError { errHTTPCode = 414
+                    , errReasonPhrase = "Request-URI Too Large"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err415' Unsupported Media Type
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err415 { errBody = "Supported media types:  gif, png" }
+--
+err415 :: ServerError
+err415 = ServerError { errHTTPCode = 415
+                    , errReasonPhrase = "Unsupported Media Type"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err416' Request range not satisfiable
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err416 { errBody = "Valid range is [0, 424242]." }
+--
+err416 :: ServerError
+err416 = ServerError { errHTTPCode = 416
+                    , errReasonPhrase = "Request range not satisfiable"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err417' Expectation Failed
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err417 { errBody = "I found a quux in the request.  This isn't going to work." }
+--
+err417 :: ServerError
+err417 = ServerError { errHTTPCode = 417
+                    , errReasonPhrase = "Expectation Failed"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err418' Expectation Failed
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err418 { errBody = "Apologies, this is not a webserver but a teapot." }
+--
+err418 :: ServerError
+err418 = ServerError { errHTTPCode = 418
+                    , errReasonPhrase = "I'm a teapot"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err422' Unprocessable Entity
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err422 { errBody = "I understood your request, but can't process it." }
+--
+err422 :: ServerError
+err422 = ServerError { errHTTPCode = 422
+                    , errReasonPhrase = "Unprocessable Entity"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err500' Internal Server Error
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err500 { errBody = "Exception in module A.B.C:55.  Have a great day!" }
+--
+err500 :: ServerError
+err500 = ServerError { errHTTPCode = 500
+                    , errReasonPhrase = "Internal Server Error"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err501' Not Implemented
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err501 { errBody = "/v1/foo is not supported with quux in the request." }
+--
+err501 :: ServerError
+err501 = ServerError { errHTTPCode = 501
+                    , errReasonPhrase = "Not Implemented"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err502' Bad Gateway
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err502 { errBody = "Tried gateway foo, bar, and baz.  None responded." }
+--
+err502 :: ServerError
+err502 = ServerError { errHTTPCode = 502
+                    , errReasonPhrase = "Bad Gateway"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err503' Service Unavailable
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err503 { errBody = "We're rewriting in PHP." }
+--
+err503 :: ServerError
+err503 = ServerError { errHTTPCode = 503
+                    , errReasonPhrase = "Service Unavailable"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err504' Gateway Time-out
+--
+-- Example:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err504 { errBody = "Backend foobar did not respond in 5 seconds." }
+--
+err504 :: ServerError
+err504 = ServerError { errHTTPCode = 504
+                    , errReasonPhrase = "Gateway Time-out"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
+
+-- | 'err505' HTTP Version not supported
+--
+-- Example usage:
+--
+-- > failingHandler :: Handler ()
+-- > failingHandler = throwError $ err505 { errBody = "I support HTTP/4.0 only." }
+--
+err505 :: ServerError
+err505 = ServerError { errHTTPCode = 505
+                    , errReasonPhrase = "HTTP Version not supported"
+                    , errBody = ""
+                    , errHeaders = []
+                    }
diff --git a/test/Servant/Server/Internal/RoutingApplicationSpec.hs b/test/Servant/Server/Internal/RoutingApplicationSpec.hs
--- a/test/Servant/Server/Internal/RoutingApplicationSpec.hs
+++ b/test/Servant/Server/Internal/RoutingApplicationSpec.hs
@@ -23,7 +23,7 @@
 import           Network.Wai
                  (defaultRequest)
 import           Servant
-import           Servant.Server.Internal.RoutingApplication
+import           Servant.Server.Internal
 import           Test.Hspec
 import           Test.Hspec.Wai
                  (request, shouldRespondWith, with)
diff --git a/test/Servant/Server/RouterSpec.hs b/test/Servant/Server/RouterSpec.hs
--- a/test/Servant/Server/RouterSpec.hs
+++ b/test/Servant/Server/RouterSpec.hs
@@ -30,20 +30,45 @@
 
 routerSpec :: Spec
 routerSpec = do
-  let app' :: Application
-      app' = toApplication $ runRouter router'
+  describe "tweakResponse" $ do
+    let app' :: Application
+        app' = toApplication $ runRouter router'
 
-      router', router :: Router ()
-      router' = tweakResponse (fmap twk) router
-      router = leafRouter $ \_ _ cont -> cont (Route $ responseBuilder (Status 201 "") [] "")
+        router', router :: Router ()
+        router' = tweakResponse (fmap twk) router
+        router = leafRouter $ \_ _ cont -> cont (Route $ responseBuilder (Status 201 "") [] "")
 
-      twk :: Response -> Response
-      twk (ResponseBuilder (Status i s) hs b) = ResponseBuilder (Status (i + 1) s) hs b
-      twk b = b
+        twk :: Response -> Response
+        twk (ResponseBuilder (Status i s) hs b) = ResponseBuilder (Status (i + 1) s) hs b
+        twk b = b
 
-  describe "tweakResponse" . with (return app') $ do
-    it "calls f on route result" $ do
-      get "" `shouldRespondWith` 202
+    with (return app') $ do
+      it "calls f on route result" $ do
+        get "" `shouldRespondWith` 202
+
+  describe "runRouter" $ do
+    let toApp :: Router () -> Application
+        toApp = toApplication . runRouter
+
+        cap :: Router ()
+        cap = CaptureRouter $
+          let delayed = addCapture (emptyDelayed $ Route pure) (const $ delayedFail err400)
+          in leafRouter
+             $ \env req res ->
+                 runAction delayed env req res
+                 . const
+                 $ Route success
+
+        router :: Router ()
+        router = leafRouter (\_ _ res -> res $ Route success)
+          `Choice` cap
+
+        success :: Response
+        success = responseBuilder (Status 200 "") [] ""
+
+    with (pure $ toApp router) $ do
+      it "capture failure returns a 400 response" $ do
+        get "/badcapture" `shouldRespondWith` 400
 
 distributivitySpec :: Spec
 distributivitySpec =
