diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,15 @@
 
 ## Unreleased
 
+## 0.1.0.3 - 2026-03-23
+
+### Fixed
+
+- Fixed a resource leak where an async exception delivered during `handleSharerLeave`
+  (between `getCurrentTime` and the `atomically` call, or during the `atomically` if it
+  blocks) could leave a cache entry's sharer count permanently elevated, preventing
+  eviction. `handleSharerLeave` is now wrapped in `uninterruptibleMask_`.
+
 ## 0.1.0.2 - 2026-03-23
 
 ### Fixed
diff --git a/shared-resource-cache.cabal b/shared-resource-cache.cabal
--- a/shared-resource-cache.cabal
+++ b/shared-resource-cache.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           shared-resource-cache
-version:        0.1.0.2
+version:        0.1.0.3
 synopsis:       A thread-safe cache for sharing resources across threads with automatic expiry
 description:    A cache designed for guaranteeing that different threads can share the same reference to a resource. For example, it allows threads to communicate via a shared
                 <<https://hackage.haskell.org/package/stm-2.5.3.1/docs/Control-Concurrent-STM-TChan.html TChan broadcast channel>>. 
diff --git a/src/Data/SharedResourceCache.hs b/src/Data/SharedResourceCache.hs
--- a/src/Data/SharedResourceCache.hs
+++ b/src/Data/SharedResourceCache.hs
@@ -30,6 +30,7 @@
 import Data.SharedResourceCache.Internal.ExpiringSharedResourceCache (CacheEntry(..), SharedResourceCache (..), CacheExpiryConfig, loadCacheableResource, handleSharerLeave, handlerSharerJoin, handleSharerLeaveSTM)
 import Data.SharedResourceCache.Internal.Broom (startBroomLoop)
 import Data.SharedResourceCache.Internal.Model (CacheExpiryConfig(..))
+import Control.Exception (uninterruptibleMask_)
 
 -- | Constructs a resource cache that is expected to be used for the lifetime of the program. Internally, it forks a thread to 
 --  manage periodically removing cache entries that have expired (as per the cache expiry configuration.)
@@ -56,7 +57,7 @@
     deallocateResource :: SharedResourceCache err a -> IO ()
     deallocateResource cache = do
       let threadId = cacheCleanupThreadId cache
-      killThread threadId
+      uninterruptibleMask_ (killThread threadId)
 
 -- | Executes the given action using the resource with the given ID. This function is a wrapper around the 'getCacheableResource' function,
 --   where the resource is freed at the end of the supplied action
diff --git a/src/Data/SharedResourceCache/Internal/ExpiringSharedResourceCache.hs b/src/Data/SharedResourceCache/Internal/ExpiringSharedResourceCache.hs
--- a/src/Data/SharedResourceCache/Internal/ExpiringSharedResourceCache.hs
+++ b/src/Data/SharedResourceCache/Internal/ExpiringSharedResourceCache.hs
@@ -35,9 +35,12 @@
         removeScheduledCleanup (cleanUpMap cache) resourceId
 
     handleSharerLeave :: SharedResourceCache err a -> CacheItem a -> Text -> IO ()
-    handleSharerLeave cache cacheItem resourceId = do
-        now <- getCurrentTime
-        atomically (handleSharerLeaveSTM cache cacheItem resourceId now)
+    handleSharerLeave cache cacheItem resourceId = 
+        -- We wrap this in an uninterruptibleMask_ so that we don't end up with items in the cache with phantom
+        -- connections if the thread is killed during a blocking operation
+        uninterruptibleMask_ $ do
+            now <- getCurrentTime
+            atomically (handleSharerLeaveSTM cache cacheItem resourceId now)
 
     handleSharerLeaveSTM  :: SharedResourceCache err a -> CacheItem a -> Text -> UTCTime -> STM ()
     handleSharerLeaveSTM (SharedResourceCache cache cleanUpMap _ _ _ config) cacheItem resourceId now = do
