diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,13 @@
 # ChangeLog for warp
 
+## 3.4.14
+
+* Important bugfix to not deadlock on empty file descriptors if the cause of
+  the file descriptor exhaustion is outside of the server's control.
+  (i.e. the server does not have any running connections and can't use a file
+  descriptor to create the next connection)
+  [#1084](https://github.com/yesodweb/wai/pull/1084)
+
 ## 3.4.13.1
 
 * Bugfix to fall back to "blocking `recv`" when on Windows systems and when
diff --git a/Network/Wai/Handler/Warp/Counter.hs b/Network/Wai/Handler/Warp/Counter.hs
--- a/Network/Wai/Handler/Warp/Counter.hs
+++ b/Network/Wai/Handler/Warp/Counter.hs
@@ -3,6 +3,7 @@
 module Network.Wai.Handler.Warp.Counter (
     Counter,
     newCounter,
+    HasDecreased (..),
     waitForZero,
     increase,
     decrease,
@@ -25,12 +26,18 @@
     x <- readTVar var
     when (x > 0) retry
 
-waitForDecreased :: Counter -> IO ()
+data HasDecreased = HasDecreased | NoConnections
+    deriving (Eq, Show)
+
+waitForDecreased :: Counter -> IO HasDecreased
 waitForDecreased (Counter var) = do
     n0 <- atomically $ readTVar var
-    atomically $ do
-        n <- readTVar var
-        check (n < n0)
+    if n0 <= 0
+        then pure NoConnections
+        else atomically $ do
+            n <- readTVar var
+            check (n < n0)
+            pure HasDecreased
 
 increase :: Counter -> IO ()
 increase (Counter var) = atomically $ modifyTVar' var $ \x -> x + 1
diff --git a/Network/Wai/Handler/Warp/Run.hs b/Network/Wai/Handler/Warp/Run.hs
--- a/Network/Wai/Handler/Warp/Run.hs
+++ b/Network/Wai/Handler/Warp/Run.hs
@@ -21,7 +21,7 @@
 import qualified Control.Exception as E
 import qualified Data.ByteString as S
 import Data.Functor (($>))
-import Data.IORef (newIORef, readIORef)
+import Data.IORef (newIORef, readIORef, IORef, writeIORef)
 import Data.Streaming.Network (bindPortTCP)
 import Foreign.C.Error (Errno (..), eCONNABORTED, eMFILE)
 import GHC.Conc.Sync (labelThread, myThreadId)
@@ -266,8 +266,9 @@
 runSettingsConnectionMakerSecure oldSettings getConnMaker app = do
     settingsBeforeMainLoop oldSettings
     (ServerState{serverConnectionCounter}, newSettings) <- makeServerState oldSettings
-    withII newSettings $
-        acceptConnection newSettings getConnMaker app serverConnectionCounter
+    withII newSettings $ \ii ->
+        initFdExhaustionRef >>=
+            acceptConnection newSettings getConnMaker app serverConnectionCounter ii
 
 -- | Running an action with internal info.
 --
@@ -311,8 +312,12 @@
     -> Application
     -> Counter
     -> InternalInfo
+    -> IORef FdExhaustion
+        -- ^ This ref will be used to "debounce" the call to 'settingsOnException'
+        -- when we hit an 'IOError' with 'eMFILE' in the case that Warp is not
+        -- the reason the file descriptors are exhausted.
     -> IO ()
-acceptConnection set getConnMaker app counter ii = do
+acceptConnection set getConnMaker app counter ii fdRef = do
     -- First mask all exceptions in acceptLoop. This is necessary to
     -- ensure that no async exception is throw between the call to
     -- acceptNewConnection and the registering of connClose.
@@ -345,19 +350,44 @@
     acceptNewConnection = do
         ex <- E.try getConnMaker
         case ex of
-            Right x -> return $ Just x
+            Right x -> do
+                -- Important to mark the exhaustion issue to be resolved
+                -- when we get connections again.
+                resetFdExhaustion fdRef
+                return $ Just x
             Left e -> do
                 let getErrno (Errno cInt) = cInt
                     isErrno err = ioe_errno e == Just (getErrno err)
-                if | isErrno eCONNABORTED -> acceptNewConnection
+                if | isErrno eCONNABORTED -> do
+                        -- Important to mark the exhaustion issue to be resolved
+                        resetFdExhaustion fdRef
+                        acceptNewConnection
+                     -- Keep in mind to reset the ref when anything other
+                     -- than this branch runs
                    | isErrno eMFILE -> do
-                       settingsOnException set Nothing $ E.toException e
-                       waitForDecreased counter
-                       acceptNewConnection
+                        handleFdExhaustion e
+                        acceptNewConnection
                    | otherwise -> do
-                       settingsOnException set Nothing $ E.toException e
-                       return Nothing
+                        -- Maybe not important to mark the exhaustion issue
+                        -- as resolved here, but just for completeness' sake.
+                        resetFdExhaustion fdRef
+                        settingsOnException set Nothing $ E.toException e
+                        return Nothing
 
+    handleFdExhaustion e = do
+        fdExhaustion <- readIORef fdRef
+        -- If file descriptors are exhausted while Warp has
+        -- no current connections, 'settingsOnException' would
+        -- get called an enormous amount of times per second.
+        when (fdExhaustion /= FdExhausted) $
+            settingsOnException set Nothing $ E.toException e
+        hasDecreased <- waitForDecreased counter
+        -- If we get 'NoConnections', that means the file
+        -- descriptor exhaustion is outside of our control.
+        -- We flag it so that 'settingsOnException' doesn't get
+        -- called until the exhaustion issue is resolved.
+        when (hasDecreased == NoConnections) $ setFdExhaustion fdRef
+
 -- Fork a new worker thread for this connection maker, and ask for a
 -- function to unmask (i.e., allow async exceptions to be thrown).
 fork
@@ -494,3 +524,15 @@
             Nothing -> pure ()
             Just ServerState{serverShuttingDown} ->
                 writeShuttingDown serverShuttingDown True
+
+data FdExhaustion = NoFdIssue | FdExhausted
+    deriving (Eq, Show)
+
+initFdExhaustionRef :: IO (IORef FdExhaustion)
+initFdExhaustionRef = newIORef NoFdIssue
+
+resetFdExhaustion :: IORef FdExhaustion -> IO ()
+resetFdExhaustion = flip writeIORef NoFdIssue
+
+setFdExhaustion :: IORef FdExhaustion -> IO ()
+setFdExhaustion = flip writeIORef FdExhausted
diff --git a/warp.cabal b/warp.cabal
--- a/warp.cabal
+++ b/warp.cabal
@@ -1,6 +1,6 @@
 cabal-version:      >=1.10
 name:               warp
-version:            3.4.13.1
+version:            3.4.14
 license:            MIT
 license-file:       LICENSE
 maintainer:         michael@snoyman.com
