diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,19 @@
+# v1.5.1.1
+
+## Fixes
+
+- After the server closes an idle pooled connection (a Postgres restart,
+  `pg_terminate_backend`, a server-side idle timeout), the first session on
+  it can fail with `UnexpectedRowCountStatementError` instead of a connection
+  error. Hasql maps the extra result that reports the closed connection to
+  that error, and a `singleRow` statement that returns two rows fails the
+  same way on a healthy connection, so the error alone cannot justify a
+  discard: the dead connection got returned to the pool and failed one more
+  session before it was discarded, so after a database restart every pooled
+  connection failed two sessions. Now, when a session fails with an error
+  that does not require a discard, `use` also checks the status that the
+  adapter reports for the connection and discards it when it is marked bad.
+
 # v1.5.1.0
 
 ## Fixes
diff --git a/hasql-pool.cabal b/hasql-pool.cabal
--- a/hasql-pool.cabal
+++ b/hasql-pool.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name: hasql-pool
-version: 1.5.1.0
+version: 1.5.1.1
 category: Hasql, Database, PostgreSQL
 synopsis: Pool of connections for Hasql
 homepage: https://github.com/nikita-volkov/hasql-pool
diff --git a/src/integration-tests/Specs/UseSpec.hs b/src/integration-tests/Specs/UseSpec.hs
--- a/src/integration-tests/Specs/UseSpec.hs
+++ b/src/integration-tests/Specs/UseSpec.hs
@@ -39,6 +39,27 @@
         _ -> False
       use pool (Sessions.getSetting settingName) `shouldReturn` Right Nothing
 
+  -- After a server restart or @pg_terminate_backend@ the first statement on
+  -- the closed connection can fail with an 'Errors.UnexpectedRowCountStatementError'
+  -- instead of a 'Errors.ConnectionSessionError'. The pool must not hand the
+  -- connection out again.
+  it "Connections closed by the server cause eviction of connection" \scopeParams ->
+    Scripts.onAutotaggedPool 1 10 1_800 1_800 scopeParams \appName pool -> do
+      use pool Sessions.selectOne `shouldReturn` Right 1
+      Scripts.onAutotaggedPool 1 10 1_800 1_800 scopeParams \_ adminPool ->
+        use adminPool (terminateBackends appName) `shouldReturn` Right [True]
+      _ <- use pool Sessions.selectOne
+      use pool Sessions.selectOne `shouldReturn` Right 1
+
+  it "Unexpected row counts on a healthy connection don't cause eviction of connection" \scopeParams ->
+    Scripts.onAutotaggedPool 1 10 1_800 1_800 scopeParams \_ pool -> do
+      Right pid <- use pool backendPid
+      result <- use pool selectTwoRowsAsSingleRow
+      result `shouldSatisfy` \case
+        Left (SessionUsageError (Errors.StatementSessionError _ _ _ _ _ Errors.UnexpectedRowCountStatementError {})) -> True
+        _ -> False
+      use pool backendPid `shouldReturn` Right pid
+
   it "Connection gets returned to the pool after normal use" \scopeParams ->
     Scripts.onAutotaggedPool 1 10 1_800 1_800 scopeParams \_ pool -> do
       _ <- use pool $ Sessions.selectOne
@@ -173,6 +194,36 @@
         ("select $1 :: " <> quoteIdentifier typeName)
         (Encoders.param (Encoders.nonNullable (Encoders.enum Nothing typeName id)))
         (Decoders.singleRow (Decoders.column (Decoders.nonNullable (Decoders.enum Nothing typeName Just))))
+
+terminateBackends :: Text -> Session.Session [Bool]
+terminateBackends appName =
+  Session.statement appName statement
+  where
+    statement =
+      Statement.unpreparable
+        "select pg_terminate_backend(pid, 10000) from pg_stat_activity where application_name = $1"
+        (Encoders.param (Encoders.nonNullable Encoders.text))
+        (Decoders.rowList (Decoders.column (Decoders.nonNullable Decoders.bool)))
+
+backendPid :: Session.Session Int32
+backendPid =
+  Session.statement () statement
+  where
+    statement =
+      Statement.preparable
+        "select pg_backend_pid()"
+        Encoders.noParams
+        (Decoders.singleRow (Decoders.column (Decoders.nonNullable Decoders.int4)))
+
+selectTwoRowsAsSingleRow :: Session.Session Int64
+selectTwoRowsAsSingleRow =
+  Session.statement () statement
+  where
+    statement =
+      Statement.preparable
+        "select * from (values (1 :: int8), (2 :: int8)) as t"
+        Encoders.noParams
+        (Decoders.singleRow (Decoders.column (Decoders.nonNullable Decoders.int8)))
 
 driverError :: Session.Session ()
 driverError =
diff --git a/src/library/Hasql/Pool.hs b/src/library/Hasql/Pool.hs
--- a/src/library/Hasql/Pool.hs
+++ b/src/library/Hasql/Pool.hs
@@ -193,7 +193,8 @@
 -- | Use a connection from the pool to run a session and return the connection
 -- to the pool, when finished.
 --
--- Session failing with a 'Session.ClientError' gets interpreted as a loss of
+-- Session failing with a 'Errors.ConnectionSessionError' or leaving the
+-- connection in the 'Pqi.ConnectionBad' state gets interpreted as a loss of
 -- connection. In such case the connection does not get returned to the pool
 -- and a slot gets freed up for a new connection to be established the next
 -- time one is needed. The error still gets returned from this function.
@@ -346,8 +347,12 @@
         Left exc -> do
           returnConn reuseVar entry
           throwIO exc
-        Right (Left err) ->
-          if ErrorsDestruction.requiresConnectionDiscard err
+        Right (Left err) -> do
+          discard <-
+            if ErrorsDestruction.requiresConnectionDiscard err
+              then return True
+              else connectionIsLost (entryConnection entry)
+          if discard
             then do
               Connection.release (entryConnection entry)
               atomically returnSlot
@@ -355,7 +360,7 @@
                 $ poolObserver
                   ( ConnectionObservation
                       (entryId entry)
-                      (TerminatedConnectionStatus (NetworkErrorConnectionTerminationReason (ErrorsDestruction.discardDetails err)))
+                      (TerminatedConnectionStatus (NetworkErrorConnectionTerminationReason (Just (Errors.toMessage err))))
                   )
               return $ Left $ SessionUsageError err
             else do
@@ -387,6 +392,26 @@
             Connection.release (entryConnection entry)
             atomically returnSlot
             poolObserver (ConnectionObservation (entryId entry) (TerminatedConnectionStatus ReleaseConnectionTerminationReason))
+
+-- | Whether the adapter has marked the connection as lost.
+--
+-- When the server closes an idle connection, e.g. on restart or with
+-- @pg_terminate_backend@, the next statement on it can fail with an error
+-- that doesn't say so: 'Errors.UnexpectedRowCountStatementError', for the
+-- extra result that reports the closed connection. A statement returning more
+-- rows than its decoder expects fails the same way on a healthy connection, so
+-- the error can't tell the two apart. The connection status can: the adapter
+-- has set it to 'Pqi.ConnectionBad' by then.
+connectionIsLost :: Connection -> IO Bool
+connectionIsLost connection =
+  Connection.use connection checkStatus <&> \case
+    Left _ -> True
+    Right lost -> lost
+  where
+    checkStatus =
+      Session.onLibpqConnection \pqConnection -> do
+        status <- Pqi.status pqConnection
+        return (Right (status == Pqi.ConnectionBad), pqConnection)
 
 -- | Whether a pool request is waiting. The ones that stopped waiting get
 -- dropped.
diff --git a/src/library/Hasql/Pool/SessionErrorDestructors.hs b/src/library/Hasql/Pool/SessionErrorDestructors.hs
--- a/src/library/Hasql/Pool/SessionErrorDestructors.hs
+++ b/src/library/Hasql/Pool/SessionErrorDestructors.hs
@@ -19,12 +19,6 @@
   -- cleanup after an interruption fails, so it must not be reused by the pool.
   Errors.DriverSessionError {} -> True
 
-discardDetails :: Errors.SessionError -> Maybe Text
-discardDetails err =
-  if requiresConnectionDiscard err
-    then Just $ Errors.toMessage err
-    else Nothing
-
 statementRequiresConnectionDiscard :: Errors.StatementError -> Bool
 statementRequiresConnectionDiscard = \case
   Errors.ServerStatementError serverError -> isStaleServerError serverError
