diff --git a/simple-parser.cabal b/simple-parser.cabal
--- a/simple-parser.cabal
+++ b/simple-parser.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 4bbc9417c33617079e899a0ae2e27cc738c1e6b0f4ed0ada613ea049b5d36317
+-- hash: 31d2be3a928dd5d492fb80583be7ad1ca75b40f617f793ad6a069146084b3702
 
 name:           simple-parser
-version:        0.8.4
+version:        0.9
 synopsis:       Simple parser combinators
 description:    Please see the README on GitHub at <https://github.com/ejconlon/simple-parser#readme>
 category:       Parsing
@@ -43,6 +43,7 @@
       SimpleParser.Result
       SimpleParser.Stack
       SimpleParser.Stream
+      SimpleParser.Throw
   other-modules:
       Paths_simple_parser
   hs-source-dirs:
@@ -69,6 +70,7 @@
     , bytestring ==0.10.*
     , containers ==0.6.*
     , errata ==0.3.*
+    , exceptions ==0.10.*
     , mmorph ==1.1.*
     , mtl ==2.2.*
     , nonempty-containers ==0.3.*
@@ -106,6 +108,7 @@
     , bytestring ==0.10.*
     , containers ==0.6.*
     , errata ==0.3.*
+    , exceptions ==0.10.*
     , mmorph ==1.1.*
     , mtl ==2.2.*
     , nonempty-containers ==0.3.*
diff --git a/src/SimpleParser.hs b/src/SimpleParser.hs
--- a/src/SimpleParser.hs
+++ b/src/SimpleParser.hs
@@ -15,6 +15,7 @@
   , module SimpleParser.Result
   , module SimpleParser.Stack
   , module SimpleParser.Stream
+  , module SimpleParser.Throw
   ) where
 
 import SimpleParser.CharString
@@ -28,3 +29,4 @@
 import SimpleParser.Result
 import SimpleParser.Stack
 import SimpleParser.Stream
+import SimpleParser.Throw
diff --git a/src/SimpleParser/Errata.hs b/src/SimpleParser/Errata.hs
--- a/src/SimpleParser/Errata.hs
+++ b/src/SimpleParser/Errata.hs
@@ -12,7 +12,7 @@
 import qualified Data.Text as T
 import Errata (Block, Style, blockMerged')
 import SimpleParser.Explain (ErrorExplanation (..), Explainable, ParseErrorExplanation (..), explainParseError)
-import SimpleParser.Result (ParseError, ParseResult (..))
+import SimpleParser.Result (ParseError, ParseErrorBundle (ParseErrorBundle), ParseResult (..))
 import SimpleParser.Stream (Col (..), HasLinePos (..), Line (..), Pos, Span (..))
 
 type LinePosExplainable l s e = (Explainable l s e, HasLinePos (Pos s))
@@ -49,5 +49,5 @@
 errataParseResult :: LinePosExplainable l s e => Style -> FilePath -> ParseResult l s e a -> [Block]
 errataParseResult style fp pr =
   case pr of
-    ParseResultError errs -> fmap (errataParseError style fp) (toList errs)
+    ParseResultError (ParseErrorBundle errs) -> fmap (errataParseError style fp) (toList errs)
     ParseResultSuccess _ -> []
diff --git a/src/SimpleParser/Input.hs b/src/SimpleParser/Input.hs
--- a/src/SimpleParser/Input.hs
+++ b/src/SimpleParser/Input.hs
@@ -27,12 +27,13 @@
 import Data.Maybe (isNothing)
 import SimpleParser.Chunked (Chunked (..))
 import SimpleParser.Parser (ParserT (..), markWithOptStateParser, markWithStateParser)
-import SimpleParser.Result (CompoundError (..), ParseError (..), ParseResult (..), RawError (..), StreamError (..))
+import SimpleParser.Result (CompoundError (..), ParseError (..), ParseErrorBundle (..), ParseResult (..), RawError (..),
+                            StreamError (..))
 import SimpleParser.Stack (emptyStack)
 import SimpleParser.Stream (Stream (..))
 
 throwStreamError :: Monad m => RawError (Chunk s) (Token s) -> ParserT l s e m a
-throwStreamError re = ParserT (\s -> pure (Just (ParseResultError (pure (ParseError emptyStack s (CompoundErrorStream (StreamError re)))))))
+throwStreamError re = ParserT (\s -> pure (Just (ParseResultError (ParseErrorBundle (pure (ParseError emptyStack s (CompoundErrorStream (StreamError re))))))))
 
 -- | Fetches the next token from the stream and runs the callback.
 withToken :: (Stream s, Monad m) => Maybe l -> (Maybe (Token s) -> ParserT l s e m a) -> ParserT l s e m a
diff --git a/src/SimpleParser/Interactive.hs b/src/SimpleParser/Interactive.hs
--- a/src/SimpleParser/Interactive.hs
+++ b/src/SimpleParser/Interactive.hs
@@ -16,7 +16,7 @@
 import SimpleParser.Explain (Explainable, buildAllParseErrorExplanations, explainParseError)
 import SimpleParser.Input (matchEnd)
 import SimpleParser.Parser (Parser, runParser)
-import SimpleParser.Result (ParseResult (..), ParseSuccess (..))
+import SimpleParser.Result (ParseErrorBundle (..), ParseResult (..), ParseSuccess (..))
 import SimpleParser.Stream (LinePosStream, newLinePosStream)
 import qualified Text.Builder as TB
 
@@ -30,7 +30,7 @@
   case runParser (parser <* matchEnd) (newLinePosStream (T.pack input)) of
     Nothing ->
       putStrLn "No result."
-    Just (ParseResultError es) ->
+    Just (ParseResultError (ParseErrorBundle es)) ->
       case errStyle of
         ErrorStyleErrata ->
           let blocks = fmap (errataParseError fancyStyle "<interactive>") (toList es)
diff --git a/src/SimpleParser/Parser.hs b/src/SimpleParser/Parser.hs
--- a/src/SimpleParser/Parser.hs
+++ b/src/SimpleParser/Parser.hs
@@ -46,8 +46,8 @@
 import Data.Text (Text)
 import qualified Data.Text as T
 import SimpleParser.Chunked (Chunked (..))
-import SimpleParser.Result (CompoundError (..), Mark (..), ParseError (..), ParseResult (..), ParseSuccess (..),
-                            markParseError, parseErrorResume, unmarkParseError)
+import SimpleParser.Result (CompoundError (..), Mark (..), ParseError (..), ParseErrorBundle (..), ParseResult (..),
+                            ParseSuccess (..), markParseError, parseErrorResume, unmarkParseError)
 import SimpleParser.Stack (emptyStack)
 
 -- | A 'ParserT' is a state/error/list transformer useful for parsing.
@@ -100,15 +100,15 @@
       Just res1 ->
         case res1 of
           ParseResultSuccess _ -> pure mres1
-          ParseResultError es1 -> runParserT two s >>= go2 (NESeq.toSeq es1)
+          ParseResultError (ParseErrorBundle es1) -> runParserT two s >>= go2 (NESeq.toSeq es1)
 
   go2 es1 mres2 =
     case mres2 of
-      Nothing -> pure (fmap ParseResultError (NESeq.nonEmptySeq es1))
+      Nothing -> pure (fmap (ParseResultError . ParseErrorBundle) (NESeq.nonEmptySeq es1))
       Just res2 ->
         case res2 of
           ParseResultSuccess _ -> pure mres2
-          ParseResultError es2 -> pure (Just (ParseResultError (es1 ><| es2)))
+          ParseResultError (ParseErrorBundle es2) -> pure (Just (ParseResultError (ParseErrorBundle (es1 ><| es2))))
 
 -- | Yields the LONGEST string of 0 or more successes of the given parser.
 -- Failures will be silenced.
@@ -166,7 +166,7 @@
             ParseResultSuccess _ ->
               -- Nothing to catch, yield existing success
               pure mres
-            ParseResultError es ->
+            ParseResultError (ParseErrorBundle es) ->
               -- Find first custom error to handle
               goSplit s0 Empty (NESeq.toSeq es)
 
@@ -174,7 +174,7 @@
       case seqPartition extractCustomError afterEs of
         Nothing ->
           -- No next custom error, finally yield all other errors
-          pure (maybe empty (pure . ParseResultError) (NESeq.nonEmptySeq (beforeEs <> afterEs)))
+          pure (maybe empty (pure . ParseResultError . ParseErrorBundle) (NESeq.nonEmptySeq (beforeEs <> afterEs)))
         Just sep ->
           -- Found custom error - handle it
           goHandle s0 beforeEs sep
@@ -195,13 +195,13 @@
             Just res ->
               case res of
                 ParseResultSuccess _ -> pure mres
-                ParseResultError es ->
+                ParseResultError (ParseErrorBundle es) ->
                   -- Add to list of errors and find next custom error
                   goSplit s0 (beforeEs <> nextBeforeEs <> NESeq.toSeq es) afterEs
 
 -- | Throws a custom error
 throwParser :: Monad m => e -> ParserT l s e m a
-throwParser e = ParserT (\s -> pure (Just (ParseResultError (pure (ParseError emptyStack s (CompoundErrorCustom e))))))
+throwParser e = ParserT (\s -> pure (Just (ParseResultError (ParseErrorBundle (pure (ParseError emptyStack s (CompoundErrorCustom e)))))))
 
 -- | Catches a custom error
 catchParser :: Monad m => ParserT l s e m a -> (e -> ParserT l s e m a) -> ParserT l s e m a
@@ -213,7 +213,7 @@
 
 -- | A simple failing parser
 failParser :: Monad m => Text -> ParserT l s e m a
-failParser msg = ParserT (\s -> pure (Just (ParseResultError (pure (ParseError emptyStack s (CompoundErrorFail msg))))))
+failParser msg = ParserT (\s -> pure (Just (ParseResultError (ParseErrorBundle (pure (ParseError emptyStack s (CompoundErrorFail msg)))))))
 
 instance Monad m => MonadFail (ParserT l s e m) where
   fail = failParser . T.pack
@@ -271,7 +271,7 @@
 markParser ml parser = ParserT (\s -> fmap (fmap (go s)) (runParserT parser s)) where
   go s res =
     case res of
-      ParseResultError es -> ParseResultError (fmap (markParseError (Mark ml s)) es)
+      ParseResultError (ParseErrorBundle es) -> ParseResultError (ParseErrorBundle (fmap (markParseError (Mark ml s)) es))
       ParseResultSuccess _ -> res
 
 -- | Like 'markParser' but allows you to mutate state. See 'withToken' and 'withChunk'.
@@ -288,7 +288,7 @@
 unmarkParser parser = ParserT (fmap (fmap go) . runParserT parser) where
   go res =
     case res of
-      ParseResultError es -> ParseResultError (fmap unmarkParseError es)
+      ParseResultError (ParseErrorBundle es) -> ParseResultError (ParseErrorBundle (fmap unmarkParseError es))
       ParseResultSuccess _ -> res
 
 -- | If the first parser succeeds in the initial state, yield results from the second parser in the initial
diff --git a/src/SimpleParser/Result.hs b/src/SimpleParser/Result.hs
--- a/src/SimpleParser/Result.hs
+++ b/src/SimpleParser/Result.hs
@@ -13,15 +13,20 @@
   , unmarkParseError
   , parseErrorEnclosingLabels
   , parseErrorNarrowestSpan
+  , ParseErrorBundle (..)
+  , listParseErrors
+  , matchSoleParseError
   , ParseSuccess (..)
   , ParseResult (..)
-  , matchSoleParseError
   ) where
 
+import Control.Exception (Exception)
+import Data.Foldable (toList)
 import Data.Sequence (Seq (..))
 import qualified Data.Sequence as Seq
 import Data.Sequence.NonEmpty (NESeq (..))
 import Data.Text (Text)
+import Data.Typeable (Typeable)
 import SimpleParser.Stack (Stack (..), bottomStack, bottomUpStack, emptyStack, pushStack, topStack)
 import SimpleParser.Stream (PosStream (..), Span (..), Stream (..))
 
@@ -74,6 +79,13 @@
   , peError :: !(CompoundError s e)
   }
 
+deriving instance (Eq l, Eq s, Eq (Token s), Eq (Chunk s), Eq e) => Eq (ParseError l s e)
+deriving instance (Show l, Show s, Show (Token s), Show (Chunk s), Show e) => Show (ParseError l s e)
+
+instance (
+  Typeable l, Typeable s, Typeable (Token s), Typeable (Chunk s), Typeable e,
+  Show l, Show s, Show (Token s), Show (Chunk s), Show e) => Exception (ParseError l s e)
+
 -- | Returns the resumption point of the 'ParseError'.
 -- If it has been marked, we use that, otherwise we assume it starts at the exact error point.
 parseErrorResume :: ParseError l s e -> s
@@ -105,27 +117,37 @@
     Empty -> Empty
     _ :<| s -> s >>= \(Mark ml _) -> maybe Seq.empty Seq.singleton ml
 
-deriving instance (Eq l, Eq s, Eq (Token s), Eq (Chunk s), Eq e) => Eq (ParseError l s e)
-deriving instance (Show l, Show s, Show (Token s), Show (Chunk s), Show e) => Show (ParseError l s e)
-
 data ParseSuccess s a = ParseSuccess
   { psEndState :: !s
   , psValue :: !a
   } deriving (Eq, Show, Functor, Foldable, Traversable)
 
-data ParseResult l s e a =
-    ParseResultError !(NESeq (ParseError l s e))
-  | ParseResultSuccess !(ParseSuccess s a)
-  deriving (Functor, Foldable, Traversable)
+newtype ParseErrorBundle l s e = ParseErrorBundle { unParseErrorBundle :: NESeq (ParseError l s e) }
 
-deriving instance (Eq l, Eq s, Eq (Token s), Eq (Chunk s), Eq e, Eq a) => Eq (ParseResult l s e a)
-deriving instance (Show l, Show s, Show (Token s), Show (Chunk s), Show e, Show a) => Show (ParseResult l s e a)
+deriving instance (Eq l, Eq s, Eq (Token s), Eq (Chunk s), Eq e) => Eq (ParseErrorBundle l s e)
+deriving instance (Show l, Show s, Show (Token s), Show (Chunk s), Show e) => Show (ParseErrorBundle l s e)
 
--- | If there is one parse error, return it, otherwise return nothing.
+instance (
+  Typeable l, Typeable s, Typeable (Token s), Typeable (Chunk s), Typeable e,
+  Show l, Show s, Show (Token s), Show (Chunk s), Show e) => Exception (ParseErrorBundle l s e)
+
+-- | Lists all errors in the bundle.
+listParseErrors :: ParseErrorBundle l s e -> [ParseError l s e]
+listParseErrors = toList . unParseErrorBundle
+
+-- | If there is only one parse error in the bundle, return it, otherwise return nothing.
 -- Errors can accumulate if you use unrestricted branching (with 'orParser' or 'Alternative' '<|>') or manual 'Parser' constructor application.
 -- However, if you always branch with 'lookAheadMatch' then you will have singleton parse errors, and this will always return 'Just'.
-matchSoleParseError :: NESeq (ParseError l s e) -> Maybe (ParseError l s e)
-matchSoleParseError es =
+matchSoleParseError :: ParseErrorBundle l s e -> Maybe (ParseError l s e)
+matchSoleParseError (ParseErrorBundle es) =
   case es of
     e :<|| Empty -> Just e
     _ -> Nothing
+
+data ParseResult l s e a =
+    ParseResultError !(ParseErrorBundle l s e)
+  | ParseResultSuccess !(ParseSuccess s a)
+  deriving (Functor, Foldable, Traversable)
+
+deriving instance (Eq l, Eq s, Eq (Token s), Eq (Chunk s), Eq e, Eq a) => Eq (ParseResult l s e a)
+deriving instance (Show l, Show s, Show (Token s), Show (Chunk s), Show e, Show a) => Show (ParseResult l s e a)
diff --git a/src/SimpleParser/Throw.hs b/src/SimpleParser/Throw.hs
new file mode 100644
--- /dev/null
+++ b/src/SimpleParser/Throw.hs
@@ -0,0 +1,38 @@
+module SimpleParser.Throw
+  ( EmptyParseError (..)
+  , runParserThrow
+  , runParserEnd
+  ) where
+
+import Control.Exception (Exception)
+import Control.Monad.Catch (MonadThrow (throwM))
+import Data.Typeable (Typeable)
+import SimpleParser.Input (matchEnd)
+import SimpleParser.Parser (Parser, runParser)
+import SimpleParser.Result (ParseResult (..), ParseSuccess (..))
+import SimpleParser.Stream (Chunk, Stream, Token)
+
+data EmptyParseError = EmptyParseError
+  deriving stock (Eq, Show)
+
+instance Exception EmptyParseError
+
+-- | Runs a parser and throws bundled errors / no parse result errors as exceptions.
+runParserThrow :: (
+  Typeable l, Typeable s, Typeable e, Typeable (Token s), Typeable (Chunk s),
+  Show l, Show s, Show e, Show (Token s), Show (Chunk s),
+  MonadThrow m) => Parser l s e a -> s -> m (ParseSuccess s a)
+runParserThrow parser s =
+  case runParser parser s of
+    Nothing -> throwM EmptyParseError
+    Just res ->
+      case res of
+        ParseResultError errs -> throwM errs
+        ParseResultSuccess success -> pure success
+
+-- | The easiest way to fully consume input and throw errors.
+runParserEnd :: (
+  Typeable l, Typeable s, Typeable e, Typeable (Token s), Typeable (Chunk s),
+  Show l, Show s, Show e, Show (Token s), Show (Chunk s),
+  Stream s, MonadThrow m) => Parser l s e a -> s -> m a
+runParserEnd parser s = fmap psValue (runParserThrow (parser <* matchEnd) s)
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -49,7 +49,7 @@
 sucRes st = Just . ParseResultSuccess . ParseSuccess st
 
 errRes :: [TestParseError] -> Maybe (TestResult a)
-errRes es = Just (ParseResultError (NESeq.unsafeFromSeq (Seq.fromList es)))
+errRes es = Just (ParseResultError (ParseErrorBundle (NESeq.unsafeFromSeq (Seq.fromList es))))
 
 custErr :: TestState -> Error -> TestParseError
 custErr endSt = ParseError emptyStack endSt . CompoundErrorCustom
