diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,10 @@
 # ChangeLog
 
+## 0.2.20
+
+* Fix recvStream overflow on pending data.
+  [#84](https://github.com/kazu-yamamoto/quic/pull/84)
+
 ## 0.2.19
 
 * Compare the minimum packet size on packet size determination.
diff --git a/Network/QUIC/Client/Run.hs b/Network/QUIC/Client/Run.hs
--- a/Network/QUIC/Client/Run.hs
+++ b/Network/QUIC/Client/Run.hs
@@ -97,6 +97,7 @@
         when (ccWatchDog conf) $ forkManaged conn $ watchDog conn
         ex <- E.try runThreads
         sendFinal conn
+        setConnectionClosed conn
         closure conn ldcc ex
   where
     open = createClientConnection conf verInfo
@@ -158,12 +159,12 @@
     let ver = chosenVersion verInfo
     initializeCoder conn InitialLevel $ initialSecrets ver peerCID
     setupCryptoStreams conn -- fixme: cleanup
-      -- RFC9000 \S14.2
-      -- "In the absence of these mechanisms, QUIC endpoints SHOULD
-      -- NOT send datagrams larger than the smallest allowed maximum
-      -- datagram size."
-      --
-      -- Thus use 1200 bytes for minimum packet size.
+    -- RFC9000 \S14.2
+    -- "In the absence of these mechanisms, QUIC endpoints SHOULD
+    -- NOT send datagrams larger than the smallest allowed maximum
+    -- datagram size."
+    --
+    -- Thus use 1200 bytes for minimum packet size.
     let pktSiz0 = fromMaybe (defaultPacketSize peersa) ccPacketSize
         pktSiz = (defaultQUICPacketSize `max` pktSiz0) `min` maximumPacketSize peersa
     setMaxPacketSize conn pktSiz
diff --git a/Network/QUIC/Connection/Misc.hs b/Network/QUIC/Connection/Misc.hs
--- a/Network/QUIC/Connection/Misc.hs
+++ b/Network/QUIC/Connection/Misc.hs
@@ -41,7 +41,6 @@
 import System.Mem.Weak
 
 import Network.QUIC.Connection.Queue
-import Network.QUIC.Connection.State
 import Network.QUIC.Connection.Timeout
 import Network.QUIC.Connection.Types
 import Network.QUIC.Connector
@@ -241,9 +240,7 @@
 -- | Closing a connection with/without a transport error.
 --   Internal threads should use this.
 closeConnection :: Connection -> TransportError -> ReasonPhrase -> IO ()
-closeConnection conn err desc = do
-    setConnectionClosed conn
-    E.throwIO quicexc
+closeConnection _conn err desc = E.throwIO quicexc
   where
     quicexc = TransportErrorIsSent err desc
 
diff --git a/Network/QUIC/Connection/Types.hs b/Network/QUIC/Connection/Types.hs
--- a/Network/QUIC/Connection/Types.hs
+++ b/Network/QUIC/Connection/Types.hs
@@ -194,12 +194,13 @@
     deriving (Show)
 
 newConcurrency :: Role -> Direction -> Int -> Concurrency
-newConcurrency rl dir n = Concurrency ini $ StreamIdBase n
+newConcurrency rl dir n = Concurrency{..}
   where
     bidi = dir == Bidirectional
-    ini
+    currentStream
         | rl == Client = if bidi then 0 else 2
         | otherwise = if bidi then 1 else 3
+    maxStreams = StreamIdBase n
 
 ----------------------------------------------------------------
 
@@ -220,7 +221,11 @@
     }
 
 newPathInfo :: SockAddr -> IO PathInfo
-newPathInfo sa = PathInfo sa <$> newTVarIO 0 <*> newTVarIO 0 <*> newTVarIO False
+newPathInfo peerSockAddr = do
+    pathBytesTx <- newTVarIO 0
+    pathBytesRx <- newTVarIO 0
+    addressValidated <- newTVarIO False
+    return PathInfo{..}
 
 ----------------------------------------------------------------
 
@@ -473,9 +478,9 @@
     }
 
 newShared :: IO Shared
-newShared =
-    Shared
-        <$> newIORef False
-        <*> newIORef False
-        <*> newIORef False
-        <*> newTQueueIO
+newShared = do
+    sharedCloseSent <- newIORef False
+    sharedCloseReceived <- newIORef False
+    shared1RTTReady <- newIORef False
+    sharedSendStreamQ <- newTQueueIO
+    return Shared{..}
diff --git a/Network/QUIC/IO.hs b/Network/QUIC/IO.hs
--- a/Network/QUIC/IO.hs
+++ b/Network/QUIC/IO.hs
@@ -180,7 +180,11 @@
 
 -- | Receiving data in the stream. In the case where a FIN is received
 --   an empty bytestring is returned.
-recvStream :: Stream -> Int -> IO ByteString
+recvStream
+    :: Stream
+    -> Int -- ^ Number of bytes to receive. In certain cases, `recvStream` can return
+           -- fewer bytes than requested, but never more bytes than requested..
+    -> IO ByteString
 recvStream s n = do
     bs <- takeRecvStreamQwithSize s n
     let len = BS.length bs
diff --git a/Network/QUIC/Recovery/Types.hs b/Network/QUIC/Recovery/Types.hs
--- a/Network/QUIC/Recovery/Types.hs
+++ b/Network/QUIC/Recovery/Types.hs
@@ -211,7 +211,13 @@
     deriving (Show)
 
 initialLossDetection :: LossDetection
-initialLossDetection = LossDetection (-1) ackInfo0 timeMicrosecond0 Nothing
+initialLossDetection =
+    LossDetection
+        { largestAckedPacket = (-1)
+        , previousAckInfo = ackInfo0
+        , timeOfLastAckElicitingPacket = timeMicrosecond0
+        , lossTime = Nothing
+        }
 
 ----------------------------------------------------------------
 
diff --git a/Network/QUIC/Server/Run.hs b/Network/QUIC/Server/Run.hs
--- a/Network/QUIC/Server/Run.hs
+++ b/Network/QUIC/Server/Run.hs
@@ -130,6 +130,7 @@
                         Right r -> return r
             ex <- E.try runThreads
             sendFinal conn
+            setConnectionClosed conn
             closure conn ldcc ex
   where
     open = createServerConnection conf dispatch acc stvar
diff --git a/Network/QUIC/Stream/Reass.hs b/Network/QUIC/Stream/Reass.hs
--- a/Network/QUIC/Stream/Reass.hs
+++ b/Network/QUIC/Stream/Reass.hs
@@ -13,8 +13,6 @@
 import qualified Data.Sequence as Seq
 
 import Network.QUIC.Imports
-
--- import Network.QUIC.Logger
 import Network.QUIC.Stream.Frag
 import Network.QUIC.Stream.Misc
 import Network.QUIC.Stream.Queue
@@ -41,7 +39,10 @@
 
 ----------------------------------------------------------------
 
-takeRecvStreamQwithSize :: Stream -> Int -> IO ByteString
+takeRecvStreamQwithSize
+    :: Stream
+    -> Int -- ^ Number of bytes to receive.
+    -> IO ByteString
 takeRecvStreamQwithSize strm siz0 = do
     eos <- getEndOfStream strm
     if eos
@@ -55,20 +56,20 @@
                         then do
                             setEndOfStream strm
                             return ""
-                        else do
-                            let len = BS.length b0
-                            case len `compare` siz0 of
-                                LT -> tryRead (siz0 - len) (b0 :)
-                                EQ -> return b0
-                                GT -> do
-                                    let (b1, b2) = BS.splitAt siz0 b0
-                                    writePendingData strm b2
-                                    return b1
+                        else handleBytes b0
                 Just b0 -> do
                     clearPendingData strm
-                    let len = BS.length b0
-                    tryRead (siz0 - len) (b0 :)
+                    handleBytes b0
   where
+    handleBytes b0 =
+        let len = BS.length b0
+         in case len `compare` siz0 of
+                LT -> tryRead (siz0 - len) (b0 :)
+                EQ -> return b0
+                GT -> do
+                    let (b1, b2) = BS.splitAt siz0 b0
+                    writePendingData strm b2
+                    return b1
     tryRead siz build = do
         mb <- tryTakeRecvStreamQ strm
         case mb of
@@ -160,9 +161,7 @@
     si0@(StreamState off0 fin0) <- readIORef streamStateRx
     let si1 = si0{streamFin = True}
     if fin0
-        then do
-            -- stdoutLogger "Illegal Fin" -- fixme
-            return True
+        then return True
         else case off `compare` off0 of
             LT -> return True
             EQ -> do
diff --git a/Network/QUIC/Stream/Types.hs b/Network/QUIC/Stream/Types.hs
--- a/Network/QUIC/Stream/Types.hs
+++ b/Network/QUIC/Stream/Types.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE RecordWildCards #-}
+
 module Network.QUIC.Stream.Types (
     Stream (..),
     newStream,
@@ -43,16 +45,18 @@
 instance Show Stream where
     show s = show $ streamId s
 
+{- FOURMOLU_DISABLE -}
 newStream :: Connection -> Int -> Int -> StreamId -> IO Stream
-newStream conn sid txLim rxLim =
-    Stream sid conn
-        <$> newTVarIO (newTxFlow txLim)
-        <*> newIORef (newRxFlow rxLim)
-        <*> newIORef emptyStreamState
-        <*> newIORef emptyStreamState
-        <*> newRecvStreamQ
-        <*> newIORef Skew.empty
-        <*> newEmptyMVar
+newStream streamConnection streamId txLim rxLim = do
+    streamFlowTx    <- newTVarIO $ newTxFlow txLim
+    streamFlowRx    <- newIORef  $ newRxFlow rxLim
+    streamStateTx   <- newIORef emptyStreamState
+    streamStateRx   <- newIORef emptyStreamState
+    streamRecvQ     <- newRecvStreamQ
+    streamReass     <- newIORef Skew.empty
+    streamSyncFinTx <- newEmptyMVar
+    return Stream{..}
+{- FOURMOLU_ENABLE -}
 
 syncFinTx :: Stream -> IO ()
 syncFinTx s = void $ tryPutMVar (streamSyncFinTx s) ()
@@ -81,7 +85,12 @@
         let n = off' - off
             bs' = BS.drop n bs
             len' = len - n
-         in RxStreamData bs' off' len' fin
+         in RxStreamData
+                { rxstrmData = bs'
+                , rxstrmOff = off'
+                , rxstrmLen = len'
+                , rxstrmFin = fin
+                }
 
 ----------------------------------------------------------------
 
@@ -92,7 +101,11 @@
     deriving (Eq, Show)
 
 emptyStreamState :: StreamState
-emptyStreamState = StreamState 0 False
+emptyStreamState =
+    StreamState
+        { streamOffset = 0
+        , streamFin = False
+        }
 
 ----------------------------------------------------------------
 
@@ -103,4 +116,8 @@
     }
 
 newRecvStreamQ :: IO RecvStreamQ
-newRecvStreamQ = RecvStreamQ <$> newTQueueIO <*> newIORef Nothing <*> newIORef False
+newRecvStreamQ = do
+    recvStreamQ <- newTQueueIO
+    pendingData <- newIORef Nothing
+    endOfStream <- newIORef False
+    return RecvStreamQ{..}
diff --git a/Network/QUIC/Types/Packet.hs b/Network/QUIC/Types/Packet.hs
--- a/Network/QUIC/Types/Packet.hs
+++ b/Network/QUIC/Types/Packet.hs
@@ -59,7 +59,11 @@
     deriving (Eq, Show)
 
 brokenVersionInfo :: VersionInfo
-brokenVersionInfo = VersionInfo Negotiation []
+brokenVersionInfo =
+    VersionInfo
+        { chosenVersion = Negotiation
+        , otherVersions = []
+        }
 
 ----------------------------------------------------------------
 
diff --git a/quic.cabal b/quic.cabal
--- a/quic.cabal
+++ b/quic.cabal
@@ -1,6 +1,6 @@
 cabal-version:      >=1.10
 name:               quic
-version:            0.2.19
+version:            0.2.20
 license:            BSD3
 license-file:       LICENSE
 maintainer:         kazu@iij.ad.jp
