diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+# v1.0.1.2
+
+## Fixes
+
+- Fixed an async exception (e.g. from `System.Timeout.timeout`) landing mid-read permanently desyncing a connection's message framing. `Transport.receiveFrame` now runs `uninterruptibleMask_`ed, so a frame is either read to completion or not started, mirroring how `pqi-ffi`'s `safe` FFI call into `libpq` is structurally immune to the same hazard. Caught by the differential coverage in `pqi-conformance` 1.0.3.0 (#5).
+
 # v1.0.1.1
 
 ## Fixes
diff --git a/pqi-native.cabal b/pqi-native.cabal
--- a/pqi-native.cabal
+++ b/pqi-native.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name: pqi-native
-version: 1.0.1.1
+version: 1.0.1.2
 category: Database, PostgreSQL
 synopsis: Native (pure-Haskell) adapter for pqi
 description:
@@ -158,5 +158,5 @@
   build-depends:
     base >=4.11 && <5,
     hspec >=2.11 && <2.12,
-    pqi-conformance ^>=1.0.2.0,
+    pqi-conformance ^>=1.0.3.0,
     pqi-native,
diff --git a/src/transport/Pqi/Native/Transport.hs b/src/transport/Pqi/Native/Transport.hs
--- a/src/transport/Pqi/Native/Transport.hs
+++ b/src/transport/Pqi/Native/Transport.hs
@@ -14,6 +14,7 @@
   )
 where
 
+import Control.Exception (uninterruptibleMask_)
 import qualified Data.ByteString as ByteString
 import qualified Data.ByteString.Char8 as ByteString.Char8
 import Data.IORef
@@ -83,8 +84,16 @@
 
 -- | Receive one framed message: its type byte and its body (the length prefix,
 -- which counts itself, is consumed).
+--
+-- Runs fully 'uninterruptibleMask_'ed: 'Socket.ByteString.recv' is
+-- interruptible even under a plain 'mask', so an async exception (e.g. from
+-- 'System.Timeout.timeout') landing between a @recv@ returning and its bytes
+-- being folded into 'readBuffer' would otherwise drop them - desyncing the
+-- connection's framing for every subsequent read. Deferring delivery across
+-- the whole frame mirrors how @pqi-ffi@'s @safe@ FFI call into @libpq@ is
+-- immune to the same hazard.
 receiveFrame :: Transport -> IO (Word8, ByteString)
-receiveFrame transport = do
+receiveFrame transport = uninterruptibleMask_ do
   header <- receiveExactly transport 5
   let typeByte = ByteString.head header
       frameLength = decodeInt32BE (ByteString.drop 1 header)
