diff --git a/System/OsString/Common.hs b/System/OsString/Common.hs
--- a/System/OsString/Common.hs
+++ b/System/OsString/Common.hs
@@ -143,7 +143,7 @@
 where
 
 
-
+import System.OsString.Internal.Exception
 import System.OsString.Internal.Types (
 #ifdef WINDOWS
   WindowsString(..), WindowsChar(..)
@@ -236,7 +236,7 @@
            -> String
            -> Either EncodingException PLATFORM_STRING
 encodeWith enc str = unsafePerformIO $ do
-  r <- try @SomeException $ GHC.withCStringLen enc str $ \cstr -> WindowsString <$> BS8.packCStringLen cstr
+  r <- trySafe @SomeException $ GHC.withCStringLen enc str $ \cstr -> WindowsString <$> BS8.packCStringLen cstr
   evaluate $ force $ first (flip EncodingError Nothing . displayException) r
 #else
 -- | Encode a 'String' with the specified encoding.
@@ -244,7 +244,7 @@
            -> String
            -> Either EncodingException PLATFORM_STRING
 encodeWith enc str = unsafePerformIO $ do
-  r <- try @SomeException $ GHC.withCStringLen enc str $ \cstr -> PosixString <$> BSP.packCStringLen cstr
+  r <- trySafe @SomeException $ GHC.withCStringLen enc str $ \cstr -> PosixString <$> BSP.packCStringLen cstr
   evaluate $ force $ first (flip EncodingError Nothing . displayException) r
 #endif
 
@@ -340,7 +340,7 @@
            -> PLATFORM_STRING
            -> Either EncodingException String
 decodeWith winEnc (WindowsString ba) = unsafePerformIO $ do
-  r <- try @SomeException $ BS8.useAsCStringLen ba $ \fp -> GHC.peekCStringLen winEnc fp
+  r <- trySafe @SomeException $ BS8.useAsCStringLen ba $ \fp -> GHC.peekCStringLen winEnc fp
   evaluate $ force $ first (flip EncodingError Nothing . displayException) r
 #else
 -- | Decode a 'PosixString' with the specified encoding.
@@ -350,7 +350,7 @@
        -> PLATFORM_STRING
        -> Either EncodingException String
 decodeWith unixEnc (PosixString ba) = unsafePerformIO $ do
-  r <- try @SomeException $ BSP.useAsCStringLen ba $ \fp -> GHC.peekCStringLen unixEnc fp
+  r <- trySafe @SomeException $ BSP.useAsCStringLen ba $ \fp -> GHC.peekCStringLen unixEnc fp
   evaluate $ force $ first (flip EncodingError Nothing . displayException) r
 #endif
 
diff --git a/System/OsString/Encoding/Internal.hs b/System/OsString/Encoding/Internal.hs
--- a/System/OsString/Encoding/Internal.hs
+++ b/System/OsString/Encoding/Internal.hs
@@ -10,6 +10,7 @@
 
 import qualified System.OsString.Data.ByteString.Short as BS8
 import qualified System.OsString.Data.ByteString.Short.Word16 as BS16
+import System.OsString.Internal.Exception
 
 import GHC.Base
 import GHC.Real
@@ -282,13 +283,13 @@
 -- | Decode with the given 'TextEncoding'.
 decodeWithTE :: TextEncoding -> BS8.ShortByteString -> Either EncodingException String
 decodeWithTE enc ba = unsafePerformIO $ do
-  r <- try @SomeException $ BS8.useAsCStringLen ba $ \fp -> GHC.peekCStringLen enc fp
+  r <- trySafe @SomeException $ BS8.useAsCStringLen ba $ \fp -> GHC.peekCStringLen enc fp
   evaluate $ force $ first (flip EncodingError Nothing . displayException) r
 
 -- | Encode with the given 'TextEncoding'.
 encodeWithTE :: TextEncoding -> String -> Either EncodingException BS8.ShortByteString
 encodeWithTE enc str = unsafePerformIO $ do
-  r <- try @SomeException $ GHC.withCStringLen enc str $ \cstr -> BS8.packCStringLen cstr
+  r <- trySafe @SomeException $ GHC.withCStringLen enc str $ \cstr -> BS8.packCStringLen cstr
   evaluate $ force $ first (flip EncodingError Nothing . displayException) r
 
 -- -----------------------------------------------------------------------------
diff --git a/System/OsString/Internal/Exception.hs b/System/OsString/Internal/Exception.hs
new file mode 100644
--- /dev/null
+++ b/System/OsString/Internal/Exception.hs
@@ -0,0 +1,20 @@
+module System.OsString.Internal.Exception where
+
+import Control.Exception ( catch, fromException, toException, throwIO, Exception, SomeAsyncException(..) )
+
+-- | Like 'try', but rethrows async exceptions.
+trySafe :: Exception e => IO a -> IO (Either e a)
+trySafe ioA = catch action eHandler
+ where
+  action = do
+    v <- ioA
+    return (Right v)
+  eHandler e
+    | isAsyncException e = throwIO e
+    | otherwise = return (Left e)
+
+isAsyncException :: Exception e => e -> Bool
+isAsyncException e =
+    case fromException (toException e) of
+        Just (SomeAsyncException _) -> True
+        Nothing -> False
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,9 @@
 # Changelog for [`os-string` package](http://hackage.haskell.org/package/os-string)
 
+## 2.0.7 *Nov 2024*
+
+* don't catch asynchronous exceptions during encoding/decoding wrt [#22](https://github.com/haskell/os-string/issues/22)
+
 ## 2.0.6 *Jun 2024*
 
 * add `fromString` on windows
diff --git a/os-string.cabal b/os-string.cabal
--- a/os-string.cabal
+++ b/os-string.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.2
 name:               os-string
-version:            2.0.6
+version:            2.0.7
 
 -- NOTE: Don't forget to update ./changelog.md
 license:            BSD-3-Clause
@@ -50,6 +50,7 @@
     System.OsString.Encoding.Internal
     System.OsString
     System.OsString.Internal
+    System.OsString.Internal.Exception
     System.OsString.Internal.Types
     System.OsString.Posix
     System.OsString.Windows
@@ -63,7 +64,7 @@
 
   default-language: Haskell2010
   build-depends:
-    , base              >=4.12.0.0      && <4.21
+    , base              >=4.12.0.0      && <4.22
     , bytestring        >=0.11.3.0
     , deepseq
     , exceptions
diff --git a/tests/encoding/EncodingSpec.hs b/tests/encoding/EncodingSpec.hs
--- a/tests/encoding/EncodingSpec.hs
+++ b/tests/encoding/EncodingSpec.hs
@@ -14,6 +14,7 @@
 import Data.Either ( isRight )
 import qualified System.OsString.Data.ByteString.Short as BS8
 import qualified System.OsString.Data.ByteString.Short.Word16 as BS16
+import System.OsString.Internal.Exception
 import System.OsString.Encoding.Internal
 import GHC.IO (unsafePerformIO)
 import GHC.IO.Encoding ( setFileSystemEncoding )
@@ -154,21 +155,21 @@
 
 decodeP' :: BS8.ShortByteString -> Either String String
 decodeP' ba = unsafePerformIO $ do
-  r <- try @SomeException $ decodeWithBasePosix ba
+  r <- trySafe @SomeException $ decodeWithBasePosix ba
   evaluate $ force $ first displayException r
 
 encodeP' :: String -> Either String BS8.ShortByteString
 encodeP' str = unsafePerformIO $ do
-  r <- try @SomeException $ encodeWithBasePosix str
+  r <- trySafe @SomeException $ encodeWithBasePosix str
   evaluate $ force $ first displayException r
 
 decodeW' :: BS16.ShortByteString -> Either String String
 decodeW' ba = unsafePerformIO $ do
-  r <- try @SomeException $ decodeWithBaseWindows ba
+  r <- trySafe @SomeException $ decodeWithBaseWindows ba
   evaluate $ force $ first displayException r
 
 encodeW' :: String -> Either String BS8.ShortByteString
 encodeW' str = unsafePerformIO $ do
-  r <- try @SomeException $ encodeWithBaseWindows str
+  r <- trySafe @SomeException $ encodeWithBaseWindows str
   evaluate $ force $ first displayException r
 
