diff --git a/dataframe-parsing.cabal b/dataframe-parsing.cabal
--- a/dataframe-parsing.cabal
+++ b/dataframe-parsing.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.4
 name:               dataframe-parsing
-version:            1.0.2.0
+version:            1.0.2.1
 synopsis:           Shared text/binary parsing helpers for the dataframe ecosystem.
 description:
     Parsing primitives used by the @dataframe@ family: CSV-friendly text
@@ -44,7 +44,7 @@
                         bytestring-lexing >= 0.5 && < 0.6,
                         containers >= 0.6.7 && < 0.9,
                         dataframe-core ^>= 1.1,
-                        text >= 2.0 && < 3,
+                        text >= 2.1 && < 3,
                         time >= 1.12 && < 2,
                         vector >= 0.12 && < 0.14
     hs-source-dirs:     src
@@ -61,7 +61,7 @@
                         dataframe-parsing,
                         HUnit ^>= 1.6,
                         QuickCheck >= 2 && < 3,
-                        text >= 2.0 && < 3
+                        text >= 2.1 && < 3
     hs-source-dirs:     tests
     ghc-options:        -O2
     default-language:   Haskell2010
@@ -73,7 +73,7 @@
     build-depends:      base >= 4 && < 5,
                         bytestring >= 0.11 && < 0.13,
                         dataframe-parsing,
-                        text >= 2.0 && < 3,
+                        text >= 2.1 && < 3,
                         time >= 1.12 && < 2,
                         vector >= 0.12 && < 0.14
     hs-source-dirs:     bench
diff --git a/src/DataFrame/Internal/Parsing.hs b/src/DataFrame/Internal/Parsing.hs
--- a/src/DataFrame/Internal/Parsing.hs
+++ b/src/DataFrame/Internal/Parsing.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ScopedTypeVariables #-}
@@ -68,10 +69,28 @@
 {-# INLINE readInt #-}
 
 readByteStringInt :: (HasCallStack) => C.ByteString -> Maybe Int
+#if MIN_VERSION_bytestring(0,12,0)
+-- bytestring >= 0.12: 'C.readInt' returns 'Nothing' on overflow.
 readByteStringInt s = case C.readInt (C.strip s) of
-    Nothing -> Nothing
     Just (value, "") -> Just value
-    Just (_value, _) -> Nothing
+    _ -> Nothing
+#else
+-- bytestring < 0.12: 'C.readInt' silently wraps on overflow. Fields of
+-- <= 18 characters fit in an 'Int' and keep the fast path; longer ones fall
+-- back to arbitrary-precision 'C.readInteger' with a range check.
+readByteStringInt s
+    | C.length t <= 18 = case C.readInt t of
+        Just (value, "") -> Just value
+        _ -> Nothing
+    | otherwise = case C.readInteger t of
+        Just (value, "")
+            | value >= toInteger (minBound :: Int)
+            , value <= toInteger (maxBound :: Int) ->
+                Just (fromInteger value)
+        _ -> Nothing
+  where
+    t = C.strip s
+#endif
 {-# INLINE readByteStringInt #-}
 
 readByteStringDouble :: (HasCallStack) => C.ByteString -> Maybe Double
