diff --git a/CHANGES.md b/CHANGES.md
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -22,6 +22,11 @@
 For user-visible changes, see the hledger package changelog.
 
 
+# 1.43.1 2025-06-04
+
+- Hledger.Query: queryIsAmtOrSym
+
+
 # 1.43 2025-06-01
 
 - Support GHC 9.12.
diff --git a/Hledger/Data/Amount.hs b/Hledger/Data/Amount.hs
--- a/Hledger/Data/Amount.hs
+++ b/Hledger/Data/Amount.hs
@@ -196,7 +196,7 @@
 import Test.Tasty.HUnit ((@?=), assertBool, testCase)
 
 import Hledger.Data.Types
-import Hledger.Utils (colorB, numDigitsInt, numDigitsInteger)
+import Hledger.Utils (colorB, error', numDigitsInt, numDigitsInteger)
 import Hledger.Utils.Text (textQuoteIfNeeded)
 import Text.WideString (WideBuilder(..), wbFromText, wbToText, wbUnpack)
 import Data.Functor ((<&>))
@@ -333,7 +333,7 @@
    -- trace ("a1:"++showAmountDebug a1) $ trace ("a2:"++showAmountDebug a2) $ traceWith (("= :"++).showAmountDebug)
    nullamt{acommodity=c2, aquantity=q1 `op` q2, astyle=s2{asprecision=max p1 p2}}
   --  c1==c2 || q1==0 || q2==0 =
-  --  otherwise = error "tried to do simple arithmetic with amounts in different commodities"
+  --  otherwise = error' "tried to do simple arithmetic with amounts in different commodities"
 
 -- | Convert an amount to the specified commodity, ignoring and discarding
 -- any costs and assuming an exchange rate of 1.
@@ -774,9 +774,9 @@
     fromInteger = mixedAmount . fromInteger
     negate = maNegate
     (+)    = maPlus
-    (*)    = error "error, mixed amounts do not support multiplication" -- PARTIAL:
+    (*)    = error' "error, mixed amounts do not support multiplication" -- PARTIAL:
     abs    = mapMixedAmount (\amt -> amt { aquantity = abs (aquantity amt)})
-    signum = error "error, mixed amounts do not support signum"
+    signum = error' "error, mixed amounts do not support signum"
 
 -- | Calculate the key used to store an Amount within a MixedAmount.
 amountKey :: Amount -> MixedAmountKey
diff --git a/Hledger/Data/Dates.hs b/Hledger/Data/Dates.hs
--- a/Hledger/Data/Dates.hs
+++ b/Hledger/Data/Dates.hs
@@ -285,7 +285,7 @@
 dateSpanSplitLimits start _    (DateSpan (Just s) (Just e)) = (start $ fromEFDay s, fromEFDay e)
 dateSpanSplitLimits start next (DateSpan (Just s) Nothing)  = (start $ fromEFDay s, next $ start $ fromEFDay s)
 dateSpanSplitLimits start next (DateSpan Nothing  (Just e)) = (start $ fromEFDay e, next $ start $ fromEFDay e)
-dateSpanSplitLimits _     _    (DateSpan Nothing   Nothing) = error "dateSpanSplitLimits: should not be nulldatespan"  -- PARTIAL: This case should have been handled in splitSpan
+dateSpanSplitLimits _     _    (DateSpan Nothing   Nothing) = error' "dateSpanSplitLimits: should not be nulldatespan"  -- PARTIAL: This case should have been handled in splitSpan
 
 -- | Construct a list of exact 'DateSpan's from a list of boundaries, which fit within a given range.
 spansFromBoundaries :: Day -> [Day] -> [DateSpan]
diff --git a/Hledger/Data/Json.hs b/Hledger/Data/Json.hs
--- a/Hledger/Data/Json.hs
+++ b/Hledger/Data/Json.hs
@@ -28,6 +28,7 @@
 import           Text.Megaparsec (Pos, SourcePos, mkPos, unPos)
 
 import           Hledger.Data.Types
+import           Hledger.Utils.IO (error')
 import           Hledger.Data.Amount (amountsRaw, mixed)
 
 -- To JSON
@@ -290,8 +291,8 @@
 readJsonFile f = do
   bl <- BL.readFile f
   -- PARTIAL:
-  let v = fromMaybe (error $ "could not decode JSON in "++show f++" to target value")
+  let v = fromMaybe (error' $ "could not decode JSON in "++show f++" to target value")
           (decode bl :: Maybe Value)
   case fromJSON v :: FromJSON a => Result a of
-    Error e   -> error e
+    Error e   -> error' e
     Success t -> return t
diff --git a/Hledger/Query.hs b/Hledger/Query.hs
--- a/Hledger/Query.hs
+++ b/Hledger/Query.hs
@@ -44,6 +44,7 @@
   queryIsReal,
   queryIsAmt,
   queryIsSym,
+  queryIsAmtOrSym,
   queryIsStartDateOnly,
   queryIsTransactionRelated,
   -- * accessors
@@ -309,7 +310,9 @@
         case parseStatus s of Left e   -> Left $ "\"status:"++T.unpack s++"\" gave a parse error: " ++ e
                               Right st -> Right (StatusQ st, [])
 parseQueryTerm _ (T.stripPrefix "real:" -> Just s) = Right (Real $ parseBool s || T.null s, [])
-parseQueryTerm _ (T.stripPrefix "amt:" -> Just s) = Right (Amt ord q, []) where (ord, q) = either error id $ parseAmountQueryTerm s  -- PARTIAL:
+parseQueryTerm _ (T.stripPrefix "amt:" -> Just s) = case parseAmountQueryTerm s of
+  Right (ord, q) -> Right (Amt ord q, [])
+  Left err       -> Left err
 parseQueryTerm _ (T.stripPrefix "depth:" -> Just s) = (,[]) <$> parseDepthSpecQuery s
 parseQueryTerm _ (T.stripPrefix "cur:" -> Just s) = (,[]) . Sym <$> toRegexCI ("^" <> s <> "$") -- support cur: as an alias
 parseQueryTerm _ (T.stripPrefix "tag:" -> Just s) = (,[]) <$> parseTag s
@@ -587,9 +590,12 @@
 filterQuery p = simplifyQuery . filterQuery' p
 
 -- | Like filterQuery, but returns the filtered query as is, without simplifying.
+-- Note this is problematic for complex boolean queries, which if split apart 
+-- by filterQuery and then re-composed, may be altered. See eg #2371.
 filterQuery' :: (Query -> Bool) -> Query -> Query
-filterQuery' p (And qs) = And $ map (filterQuery p) qs
-filterQuery' p (Or qs) = Or $ map (filterQuery p) qs
+filterQuery' p (And qs) = And $ map (filterQuery' p) qs
+filterQuery' p (Or qs) = Or $ map (filterQuery' p) qs
+-- filterQuery' p (Or qs) = Or $ filter (not.(==Any)) $ map (filterQuery' p) qs  -- better for some, worse for others
 filterQuery' p q = if p q then q else Any
 
 -- | Remove query terms (or whole sub-expressions) from this query
@@ -679,6 +685,9 @@
 queryIsSym :: Query -> Bool
 queryIsSym (Sym _) = True
 queryIsSym _ = False
+
+queryIsAmtOrSym :: Query -> Bool
+queryIsAmtOrSym = liftA2 (||) queryIsAmt queryIsSym
 
 -- | Does this query specify a start date and nothing else (that would
 -- filter postings prior to the date) ?
diff --git a/Hledger/Reports/PostingsReport.hs b/Hledger/Reports/PostingsReport.hs
--- a/Hledger/Reports/PostingsReport.hs
+++ b/Hledger/Reports/PostingsReport.hs
@@ -106,7 +106,7 @@
           runningcalc = registerRunningCalculationFn ropts
           startnum = if historical then length precedingps + 1 else 1
           postings | historical = if sortspec_ /= defsortspec 
-                        then error "--historical and --sort should not be used together" 
+                        then error' "--historical and --sort should not be used together" 
                         else sortedps
                    | otherwise = sortedps
 
diff --git a/Hledger/Reports/ReportOptions.hs b/Hledger/Reports/ReportOptions.hs
--- a/Hledger/Reports/ReportOptions.hs
+++ b/Hledger/Reports/ReportOptions.hs
@@ -585,7 +585,9 @@
 -- We make sure to first filter by amt: and cur: terms, then value the
 -- 'Journal', then filter by the remaining terms.
 journalValueAndFilterPostings :: ReportSpec -> Journal -> Journal
-journalValueAndFilterPostings rspec j = journalValueAndFilterPostingsWith rspec j priceoracle
+journalValueAndFilterPostings rspec j =
+  -- dbg4With (\j2 -> "valuedfilteredj" <> pshow (jtxns j2)) $
+  journalValueAndFilterPostingsWith rspec j priceoracle
   where priceoracle = journalPriceOracle (infer_prices_ $ _rsReportOpts rspec) j
 
 {- [Querying before valuation]
@@ -598,11 +600,38 @@
 -}
 -- | Like 'journalValueAndFilterPostings', but takes a 'PriceOracle' as an argument.
 journalValueAndFilterPostingsWith :: ReportSpec -> Journal -> PriceOracle -> Journal
-journalValueAndFilterPostingsWith rspec@ReportSpec{_rsQuery=q, _rsReportOpts=ropts} =
-    journalApplyValuationFromOptsWith rspec . filterJournal q
+journalValueAndFilterPostingsWith = _journalValueAndFilterPostingsWith1431
+
+-- 1.42
+-- #2371 This goes wrong with complex boolean queries, splitting them apart in a lossy way.
+-- _journalValueAndFilterPostingsWith142 rspec@ReportSpec{_rsQuery=q, _rsReportOpts=ropts} j =
+--     -- Third, filter by the non amt:/cur: parts of the query
+--       filterJournalPostings' reportq
+--     -- Second, apply valuation and costing
+--     . journalApplyValuationFromOptsWith rspec
+--     -- First, filter by the amt:/cur: parts of the query, so they match pre-valuation amounts
+--       (if queryIsNull amtsymq then j else filterJournalAmounts amtsymq j)
+--   where
+--     -- with -r, replace each posting with its sibling postings
+--     filterJournalPostings' = if related_ ropts then filterJournalRelatedPostings else filterJournalPostings
+--     amtsymq = dbg1 "amtsymq" $ filterQuery queryIsAmtOrSym q
+--     reportq = dbg1 "reportq" $ filterQuery (not . queryIsAmtOrSym) q
+
+-- 1.43
+-- XXX #2396 This goes wrong with cur:. filterJournal*Postings keep all postings containing the matched commodity,
+-- but do not remove the unmatched commodities from multicommodity postings, as filterJournalAmounts would.
+-- _journalValueAndFilterPostingsWith143 rspec@ReportSpec{_rsQuery = q, _rsReportOpts = ropts} =
+--   journalApplyValuationFromOptsWith rspec .
+--   dbg1With (\j1 -> "j1" <> pshow (jtxns j1)) .
+--   (if related_ ropts then filterJournalRelatedPostings else filterJournalPostings) q
+
+-- 1.43.1
+_journalValueAndFilterPostingsWith1431 rspec@ReportSpec{_rsQuery = q, _rsReportOpts = ropts} =
+  journalApplyValuationFromOptsWith rspec . filterjournal q
   where
-    -- with -r, replace each posting with its sibling postings
-    filterJournal = if related_ ropts then filterJournalRelatedPostings else filterJournalPostings
+    filterjournal q2 =
+      filterJournalAmounts (filterQuery queryIsAmtOrSym q2) .  -- an extra amount filtering pass for #2396
+      (if related_ ropts then filterJournalRelatedPostings q2 else filterJournalPostings q2)
 
 -- | Convert this journal's postings' amounts to cost and/or to value, if specified
 -- by options (-B/--cost/-V/-X/--value etc.). Strip prices if not needed. This
@@ -643,7 +672,7 @@
     historical = DateSpan Nothing $ (fmap Exact . spanStart) =<< headMay spans
     spans = snd $ reportSpanBothDates j rspec
     styles = journalCommodityStyles j
-    err = error "journalApplyValuationFromOpts: expected all spans to have an end date"
+    err = error' "journalApplyValuationFromOpts: expected all spans to have an end date"
 
 -- | Select the Account valuation functions required for performing valuation after summing
 -- amounts. Used in MultiBalanceReport to value historical and similar reports.
@@ -662,7 +691,7 @@
         NoConversionOp -> id
         ToCost         -> styleAmounts styles . mixedAmountCost
     styles = journalCommodityStyles j
-    err = error "mixedAmountApplyValuationAfterSumFromOptsWith: expected all spans to have an end date"
+    err = error' "mixedAmountApplyValuationAfterSumFromOptsWith: expected all spans to have an end date"
 
 -- | If the ReportOpts specify that we are performing valuation after summing amounts,
 -- return Just of the commodity symbol we're converting to, Just Nothing for the default,
diff --git a/Hledger/Utils/String.hs b/Hledger/Utils/String.hs
--- a/Hledger/Utils/String.hs
+++ b/Hledger/Utils/String.hs
@@ -243,5 +243,5 @@
 stripAnsi :: String -> String
 stripAnsi s = either err id $ regexReplace ansire "" s
  where
-   err    = error "stripAnsi: invalid replacement pattern"      -- PARTIAL, shouldn't happen
+   err    = errorWithoutStackTrace "stripAnsi: invalid replacement pattern"      -- PARTIAL, shouldn't happen
    ansire = toRegex' $ T.pack "\ESC\\[([0-9]+;)*([0-9]+)?[ABCDHJKfmsu]"  -- PARTIAL, should succeed
diff --git a/hledger-lib.cabal b/hledger-lib.cabal
--- a/hledger-lib.cabal
+++ b/hledger-lib.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           hledger-lib
-version:        1.43
+version:        1.43.1
 synopsis:       A library providing the core functionality of hledger
 description:    This library contains hledger's core functionality.
                 It is used by most hledger* packages so that they support the same
@@ -34,7 +34,7 @@
 license-file:   LICENSE
 build-type:     Simple
 tested-with:
-    ghc==8.10.7, ghc==9.0.2, ghc==9.2.8, ghc==9.4.8, ghc==9.6.7, ghc==9.8.4, ghc==9.10.1
+    ghc==8.10.7, ghc==9.0.2, ghc==9.2.8, ghc==9.4.8, ghc==9.6.7, ghc==9.8.4, ghc==9.10.2, ghc==9.12.1
 extra-source-files:
     CHANGES.md
     README.md
