diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,19 @@
 # Changelog for ExchangeAlgebra
 
+## 0.4.1.2 - 2026-06-11
+
+### Fixed
+- `incomeSummaryAccount` (both `ExchangeAlgebra.Algebra.Transfer` and
+  `ExchangeAlgebra.Journal.Transfer`) crashed with `Non-exhaustive patterns in
+  case` on a **balanced ledger** (credit == debit, i.e. zero net income). In that
+  case `diffRL` reports the wildcard `Side` constructor, which the
+  `case dc of { Credit -> …; Debit -> … }` did not handle. The fix adds a `Side`
+  branch that returns the input ledger unchanged (no `NetIncome` / `NetLoss`
+  posting is appended when net income is zero). Note that appending a `Zero`
+  posting is **not** a correct alternative for the Journal version, since it is
+  not an identity there. Covered by the new `testIncomeSummaryBalancedAlg` and
+  `testIncomeSummaryBalancedJournal` regression tests.
+
 ## 0.4.1.1 - 2026-06-07
 
 ### Fixed
diff --git a/exchangealgebra.cabal b/exchangealgebra.cabal
--- a/exchangealgebra.cabal
+++ b/exchangealgebra.cabal
@@ -1,13 +1,13 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.37.0.
+-- This file has been generated from package.yaml by hpack version 0.39.1.
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: fe5fab919197e65dce7b698dc174e0447987248ecd7a81108d260e187a1a248f
+-- hash: d3a05562cbe70211374afd6c39a40fd7f8ff7e7b23b90f74716875f339171ea4
 
 name:           exchangealgebra
-version:        0.4.1.1
+version:        0.4.1.2
 synopsis:       Exchange Algebra for bookkeeping and economic simulation
 description:    Please see the README on GitHub at <https://github.com/yakagika/ExchangeAlgebra#readme>
 category:       Accounting, Finance, Math
diff --git a/src/ExchangeAlgebra/Algebra/Transfer.hs b/src/ExchangeAlgebra/Algebra/Transfer.hs
--- a/src/ExchangeAlgebra/Algebra/Transfer.hs
+++ b/src/ExchangeAlgebra/Algebra/Transfer.hs
@@ -535,12 +535,14 @@
 -- * Closing transfer entries
 
 -- | Income Summary Account: compute net income for the current period.
+-- When the ledger is balanced (credit == debit, net income is zero), @diffRL@
+-- reports the wildcard 'Side'; in that case the input is returned unchanged.
 incomeSummaryAccount :: (HatVal n, ExBaseClass b) => Alg n b -> Alg n b
 incomeSummaryAccount alg =  let (dc,diff) = diffRL alg
-                         in let x = case dc of
-                                        Credit -> diff :@ (toNot wiledcard) .~ NetIncome
-                                        Debit  -> diff :@ (toNot wiledcard) .~ NetLoss
-                         in alg .+  x
+                         in case dc of
+                                Credit -> alg .+ (diff :@ (toNot wiledcard) .~ NetIncome)
+                                Debit  -> alg .+ (diff :@ (toNot wiledcard) .~ NetLoss)
+                                Side   -> alg
 
 -- | Net income transfer. Transfers NetIncome/NetLoss to RetainedEarnings.
 --
diff --git a/src/ExchangeAlgebra/Journal/Transfer.hs b/src/ExchangeAlgebra/Journal/Transfer.hs
--- a/src/ExchangeAlgebra/Journal/Transfer.hs
+++ b/src/ExchangeAlgebra/Journal/Transfer.hs
@@ -97,14 +97,16 @@
 
 -- | Compute net income for the current period (Income Summary Account).
 -- Calculate the debit-credit difference and add it as NetIncome or NetLoss to the plank Note.
+-- When the ledger is balanced (credit == debit, net income is zero), @diffRL@ reports the
+-- wildcard 'Side'; in that case the journal is returned unchanged.
 --
 -- Complexity: O(s) (s = total number of scalar entries)
 incomeSummaryAccount :: (Note n, HatVal v, ExBaseClass b) => Journal n v b -> Journal n v b
 incomeSummaryAccount js =  let (dc,diff) = diffRL js
-                         in let x = case dc of
-                                        Credit  -> diff :@ (toNot wiledcard) .~ NetIncome
-                                        Debit -> diff :@ (toNot wiledcard) .~ NetLoss
-                         in js .+  ( x .| plank)
+                         in case dc of
+                                Credit -> js .+ ((diff :@ (toNot wiledcard) .~ NetIncome) .| plank)
+                                Debit  -> js .+ ((diff :@ (toNot wiledcard) .~ NetLoss)   .| plank)
+                                Side   -> js
 
 -- | Net income transfer (Journal version). Transfer NetIncome/NetLoss to RetainedEarnings for each Note.
 --
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -381,6 +381,36 @@
         actual = EJT.finalStockTransfer transferJournalSample
     assertEqual "Journal.finalStockTransfer matches composed transfer" (EJ.toMap ref) (EJ.toMap actual)
 
+-- A ledger whose Debit and Credit sides are equal (net income is zero).
+-- @diffRL@ reports the wildcard 'Side' here, which used to crash
+-- @incomeSummaryAccount@ with "Non-exhaustive patterns" (regression for 0.4.1.2).
+balancedTransferAlg :: TransferAlg
+balancedTransferAlg = EA.fromList
+    [ 5 :@ Not :<(Cash,  1, 1, Yen)   -- Assets  -> Debit  side
+    , 5 :@ Not :<(Sales, 2, 1, Yen)   -- Revenue -> Credit side
+    ]
+
+balancedTransferJournal :: TransferJournal
+balancedTransferJournal = balancedTransferAlg .| "balanced"
+
+-- | Regression: a balanced ledger (credit == debit) must not crash
+-- @incomeSummaryAccount@; it returns the input unchanged.
+testIncomeSummaryBalancedAlg :: IO ()
+testIncomeSummaryBalancedAlg = do
+    let actual = EAT.incomeSummaryAccount balancedTransferAlg
+    assertEqual
+        "Algebra.incomeSummaryAccount on balanced ledger returns input unchanged"
+        (EA.toList balancedTransferAlg)
+        (EA.toList actual)
+
+testIncomeSummaryBalancedJournal :: IO ()
+testIncomeSummaryBalancedJournal = do
+    let actual = EJT.incomeSummaryAccount balancedTransferJournal
+    assertEqual
+        "Journal.incomeSummaryAccount on balanced ledger returns input unchanged"
+        (EJ.toMap balancedTransferJournal)
+        (EJ.toMap actual)
+
 type SpillRestoreJournal = EJ.Journal (String, Int) Double (HatBase CountUnit)
 
 testRestoreJournalFromBinarySpill :: IO ()
@@ -802,6 +832,8 @@
     testFilterByAxisWithDeltaUpdates
     testFinalStockTransferAlgEquivalence
     testFinalStockTransferJournalEquivalence
+    testIncomeSummaryBalancedAlg
+    testIncomeSummaryBalancedJournal
     testRestoreJournalFromBinarySpill
     testSimulateEx1Default
     testCsvTranspose
