diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,18 @@
 # Revision history for stim-parser
 
+## 0.4.1.0 -- 2026-07-20
+
+* Parse DEM separator targets (`^`) inside `error(...)` instructions.
+  Added `TargetSeparator` constructor to `DEMTarget`. This is a non-breaking
+  API addition; downstream code pattern-matching on `DEMTarget` should add a
+  wildcard or handle the new constructor.
+* Require at least one target for gate and measurement instructions.
+  `parseGate` and `parseMeasure` now use `some parseQ` instead of
+  `parseExhaust parseQ`. This prevents single-letter gate/measure names such
+  as `R`, `S`, `H`, `I`, and `M` from consuming the leading character of
+  keywords like `REPEAT` and `SHIFT_COORDS` and producing misleading parse
+  errors.
+
 ## 0.4.0.0 -- 2026-07-20
 
 * Add Pauli-target support for `OBSERVABLE_INCLUDE` and `DETECTOR`
diff --git a/src/StimParser/DEM/Expr.hs b/src/StimParser/DEM/Expr.hs
--- a/src/StimParser/DEM/Expr.hs
+++ b/src/StimParser/DEM/Expr.hs
@@ -11,10 +11,12 @@
 newtype ObservableId = ObservableId { unObservableId :: Int }
   deriving (Show, Eq, Ord)
 
--- | Targets of an error instruction: detectors and/or observables.
+-- | Targets of an error instruction: detectors, observables, or the
+-- separator target (^) used to suggest a decomposition.
 data DEMTarget
     = TargetDetector !DetectorId
     | TargetObservable !ObservableId
+    | TargetSeparator
     deriving (Show, Eq)
 
 -- | A single error instruction: error(p) D0 D1 L0
diff --git a/src/StimParser/DEM/Parse.hs b/src/StimParser/DEM/Parse.hs
--- a/src/StimParser/DEM/Parse.hs
+++ b/src/StimParser/DEM/Parse.hs
@@ -31,11 +31,12 @@
   targets <- many parseDEMTarget
   return $ DEMError p targets
 
--- | Parse a single target: D0 or L0
+-- | Parse a single target: D0, L0, or ^
 parseDEMTarget :: Parser DEMTarget
 parseDEMTarget =
       TargetDetector   . DetectorId   <$> (lstring "D" *> parseInt)
   <|> TargetObservable . ObservableId <$> (lstring "L" *> parseInt)
+  <|> TargetSeparator                  <$  lstring "^"
 
 -- | Parse a detector declaration: detector(0, 0) D0
 parseDEMDetector :: Parser DEMDetector
diff --git a/src/StimParser/Parse.hs b/src/StimParser/Parse.hs
--- a/src/StimParser/Parse.hs
+++ b/src/StimParser/Parse.hs
@@ -103,7 +103,7 @@
 parseGate = do
   gty <- parseGateTy
   tag <- optional parseTag
-  qs <- parseExhaust parseQ
+  qs <- some parseQ
   return $ Gate gty tag qs
 
 parseMeasureTy :: Parser MeasureTy
@@ -124,13 +124,13 @@
       mty <- parseMeasureTy
       tag <- optional parseTag
       ph <- parsePh
-      qs <- parseExhaust parseQ
+      qs <- some parseQ
       return $ Measure mty tag (Just ph) qs
     -- parser for case: M[tag] 0
     pm2 = do 
       mty <- parseMeasureTy
       tag <- optional parseTag
-      qs <- parseExhaust parseQ
+      qs <- some parseQ
       return $ Measure mty tag Nothing qs
   -- the order is tricky - try phase version first
   try pm1 <|> pm2
diff --git a/stim-parser.cabal b/stim-parser.cabal
--- a/stim-parser.cabal
+++ b/stim-parser.cabal
@@ -20,7 +20,7 @@
 -- PVP summary:     +-+------- breaking API changes
 --                  | | +----- non-breaking API additions
 --                  | | | +--- code changes with no API change
-version:            0.4.0.0
+version:            0.4.1.0
 
 -- A short (one-line) description of the package.
 synopsis:           A parser combinator library for STIM quantum circuit files
diff --git a/test/Test/DEM/Parse.hs b/test/Test/DEM/Parse.hs
--- a/test/Test/DEM/Parse.hs
+++ b/test/Test/DEM/Parse.hs
@@ -283,50 +283,32 @@
       DEM [] ~=? run parseDEM ""
   ]
 
--- | Caret (^) rejection — '^' indicates unflattened DEM decompositions,
--- which are not part of the stable DEM format. We reject them loudly
--- instead of silently discarding them.
+-- | Caret (^) separator targets inside error(...) instructions.
 testParseDEMCaret :: Test
 testParseDEMCaret = TestList
-  [ "single caret fails" ~: TestCase $ do
-      let result = runParser parseDEM "" "error(0.01) D0 ^ D1 L0"
-      case result of
-        Left _ -> return ()
-        Right _ -> assertFailure "parser should fail on unflattened DEM with ^"
-
-  , "multiple carets fails" ~: TestCase $ do
-      let result = runParser parseDEM "" "error(0.01) D0 ^ D1 ^ D2 L0"
-      case result of
-        Left _ -> return ()
-        Right _ -> assertFailure "parser should fail on unflattened DEM with ^"
+  [ "single caret parses" ~:
+      DEM [DEMInstrError (DEMError 0.02 [TargetDetector (DetectorId 2), TargetObservable (ObservableId 0), TargetSeparator, TargetDetector (DetectorId 5), TargetDetector (DetectorId 6)])]
+      ~=? run parseDEM "error(0.02) D2 L0 ^ D5 D6\n"
 
-  , "caret between detectors fails" ~: TestCase $ do
-      let result = runParser parseDEM "" "error(0.01) D0 ^ D1 ^ D2"
-      case result of
-        Left _ -> return ()
-        Right _ -> assertFailure "parser should fail on unflattened DEM with ^"
+  , "multiple carets parse" ~:
+      DEM [DEMInstrError (DEMError 0.01 [TargetDetector (DetectorId 0), TargetSeparator, TargetDetector (DetectorId 1), TargetSeparator, TargetObservable (ObservableId 0)])]
+      ~=? run parseDEM "error(0.01) D0 ^ D1 ^ L0\n"
 
-  , "caret with observables fails" ~: TestCase $ do
-      let result = runParser parseDEM "" "error(0.01) D0 ^ L0 ^ D1"
-      case result of
-        Left _ -> return ()
-        Right _ -> assertFailure "parser should fail on unflattened DEM with ^"
+  , "caret between detectors parses" ~:
+      DEM [DEMInstrError (DEMError 0.01 [TargetDetector (DetectorId 0), TargetSeparator, TargetDetector (DetectorId 1), TargetSeparator, TargetDetector (DetectorId 2)])]
+      ~=? run parseDEM "error(0.01) D0 ^ D1 ^ D2\n"
 
-  , "caret at start fails" ~: TestCase $ do
-      let result = runParser parseDEM "" "error(0.01) ^ D0 D1"
-      case result of
-        Left _ -> return ()
-        Right _ -> assertFailure "parser should fail on unflattened DEM with ^"
+  , "caret at start parses" ~:
+      DEM [DEMInstrError (DEMError 0.01 [TargetSeparator, TargetDetector (DetectorId 0), TargetDetector (DetectorId 1)])]
+      ~=? run parseDEM "error(0.01) ^ D0 D1\n"
 
-  , "caret at end fails" ~: TestCase $ do
-      let result = runParser parseDEM "" "error(0.01) D0 D1 ^"
-      case result of
-        Left _ -> return ()
-        Right _ -> assertFailure "parser should fail on unflattened DEM with ^"
+  , "caret at end parses" ~:
+      DEM [DEMInstrError (DEMError 0.01 [TargetDetector (DetectorId 0), TargetDetector (DetectorId 1), TargetSeparator])]
+      ~=? run parseDEM "error(0.01) D0 D1 ^\n"
 
   , "no-caret error still parses" ~:
       DEM [DEMInstrError (DEMError 0.01 [TargetDetector (DetectorId 0), TargetDetector (DetectorId 1), TargetObservable (ObservableId 0)])]
-      ~=? run parseDEM "error(0.01) D0 D1 L0"
+      ~=? run parseDEM "error(0.01) D0 D1 L0\n"
   ]
 
 -- | EOF enforcement — parseDEM must consume all input
diff --git a/test/Test/Parse.hs b/test/Test/Parse.hs
--- a/test/Test/Parse.hs
+++ b/test/Test/Parse.hs
@@ -1,6 +1,7 @@
 module Test.Parse where
 
 import Test.HUnit
+import Text.Megaparsec (runParser)
 import StimParser.Parse
 import StimParser.ParseUtils (run)
 import StimParser.Expr
@@ -25,6 +26,7 @@
   , testParseFInd
   , testParseAnn
   , testParseStimNoiseOrder
+  , testParseZeroTargetRejection
   ]
 
 -- Helper to compare using show (since types don't derive Eq)
@@ -310,3 +312,29 @@
       (StimList [StimG (Gate H Nothing [Q 0, Q 1, Q 2])])
       (run parseStim "!!!Start\nH 0 1 2\n")
   ]
+
+-- | Zero-target gates/measures must fail, and keywords that share prefixes with
+-- gate/measure names must not be misparsed.
+testParseZeroTargetRejection :: Test
+testParseZeroTargetRejection = TestList
+  [ "parseGate H with no target fails" ~: TestCase $ do
+      let result = runParser parseGate "" "H"
+      case result of
+        Left _ -> return ()
+        Right _ -> assertFailure "gate with zero targets should fail"
+
+  , "parseMeasure M with no target fails" ~: TestCase $ do
+      let result = runParser parseMeasure "" "M"
+      case result of
+        Left _ -> return ()
+        Right _ -> assertFailure "measure with zero targets should fail"
+
+  , assertShowEqual "SHIFT_COORDS after TICK parses"
+      (StimList [StimAnn (Ann TICK Nothing [] []), StimAnn (Ann SHIFT_COORDS Nothing [In 0, In 0, In 1] [])])
+      (run parseStim "!!!Start\nTICK\nSHIFT_COORDS(0, 0, 1)\n")
+
+  , assertShowEqual "REPEAT after DETECTOR parses"
+      (StimList [StimAnn (Ann DETECTOR Nothing [In 0, In 0, In 0] [AnnRec (Rec (-1))]), StimRepeat 10 (StimList [StimAnn (Ann TICK Nothing [] [])])])
+      (run parseStim "!!!Start\nDETECTOR(0, 0, 0) rec[-1]\nREPEAT 10 {\nTICK\n}\n")
+  ]
+
