diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,7 @@
+# Version 0.16.1.0 (2025-08-22)
+
+- `splitTyConApp_upTo` now correctly splits apart type families. This ensures
+  it is a valid drop-in replacement for `splitTyConApp_maybe` (fixes issue #13).
 
 # Version 0.16.1.0 (2025-07-18)
 
diff --git a/ghc-tcplugin-api.cabal b/ghc-tcplugin-api.cabal
--- a/ghc-tcplugin-api.cabal
+++ b/ghc-tcplugin-api.cabal
@@ -1,6 +1,6 @@
 cabal-version:  3.0
 name:           ghc-tcplugin-api
-version:        0.16.1.0
+version:        0.16.2.0
 synopsis:       An API for type-checker plugins.
 license:        BSD-3-Clause
 build-type:     Simple
diff --git a/src/GHC/TcPlugin/API/TyConSubst.hs b/src/GHC/TcPlugin/API/TyConSubst.hs
--- a/src/GHC/TcPlugin/API/TyConSubst.hs
+++ b/src/GHC/TcPlugin/API/TyConSubst.hs
@@ -237,8 +237,8 @@
     go acc (c:cs) =
         case isCanonicalVarEq c of
           Just (var, splitAppTys -> (fn, args), NomEq)
-            | Just tyCon <- tyConAppTyCon_maybe fn ->
-                go (productive var (tyCon, args) <> acc) cs
+            | Just (tyCon, inner) <- splitTyConApp_maybe fn ->
+                go (productive var (tyCon, inner ++ args) <> acc) cs
             | Just var' <- getTyVar_maybe fn, null args ->
                 go (extendEquivClass var var' <> acc) cs
             | Just var' <- getTyVar_maybe fn, x:xs <- args ->
@@ -333,8 +333,8 @@
 splitTyConApp_upTo :: TyConSubst -> Type -> Maybe (NonEmpty (TyCon, [Type]))
 splitTyConApp_upTo subst typ = asum [
       -- Direct match
-      do tyCon <- tyConAppTyCon_maybe fn
-         return ((tyCon, args) :| [])
+      do (tyCon, inner) <- splitTyConApp_maybe fn
+         return ((tyCon, inner ++ args) :| [])
 
       -- Indirect match
     , do var <- getTyVar_maybe fn
