diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,12 @@
+### 3.1.17.0
+
+- Move `many1 :: ParsecT s u m a -> ParsecT s u m [a]` to `Text.Parsec.Prim`.
+  Drop `Stream` constraint requirement.
+- Change the position comparison in `mergeError` to not compare source names.
+  This doesn't alter reported error positions when only a single source is parsed.
+  This fixes performance issue caused by long source names.
+- Add `Exception ParseError` instance
+
 ### 3.1.16.0
 
 - Add `tokens'` and `string'` combinators which don't consume the prefix.
diff --git a/parsec.cabal b/parsec.cabal
--- a/parsec.cabal
+++ b/parsec.cabal
@@ -1,6 +1,6 @@
 cabal-version:  1.12
 name:           parsec
-version:        3.1.16.1
+version:        3.1.17.0
 
 synopsis:       Monadic parser combinators
 description:    Parsec is designed from scratch as an industrial-strength parser
@@ -26,7 +26,7 @@
 category:       Parsing
 
 build-type:     Simple
-tested-with:    GHC ==9.2.2 || ==9.0.2 || ==8.10.7 || ==8.8.4 || ==8.6.5 || ==8.4.4 || ==8.2.2 || ==8.0.2 || ==7.10.3 || ==7.8.4 || ==7.6.3 || ==7.4.2
+tested-with:    GHC ==9.8.1 || ==9.6.2 || ==9.4.7 || ==9.2.8 || ==9.0.2 || ==8.10.7 || ==8.8.4 || ==8.6.5 || ==8.4.4 || ==8.2.2 || ==8.0.2 || ==7.10.3 || ==7.8.4 || ==7.6.3 || ==7.4.2
 
 extra-source-files: ChangeLog.md, README.md
 
@@ -64,11 +64,11 @@
         Text.ParserCombinators.Parsec.Token
 
     build-depends:
-        base       >= 4.5.1.0 && < 4.19,
+        base       >= 4.5.1.0 && < 4.20,
         mtl        >= 2.1.3.1 && < 2.4,
-        bytestring >= 0.9.2.1 && < 0.12,
+        bytestring >= 0.9.2.1 && < 0.13,
         text      (>= 1.2.3.0  && < 1.3)
-               || (>= 2.0 && < 2.1)
+               || (>= 2.0 && < 2.2)
 
     default-language: Haskell2010
     other-extensions:
@@ -124,7 +124,7 @@
         mtl,
         parsec,
         -- dependencies whose version bounds are not inherited via lib:parsec
-        tasty >= 1.4 && < 1.5,
+        tasty >= 1.4 && < 1.6,
         tasty-hunit >= 0.10 && < 0.11
 
     default-language: Haskell2010
@@ -141,3 +141,17 @@
     main-is: issue127.hs
     hs-source-dirs: test
     build-depends: base, parsec
+
+test-suite parsec-issue171
+    default-language: Haskell2010
+    type: exitcode-stdio-1.0
+    main-is: issue171.hs
+    hs-source-dirs: test
+    build-depends: base, tasty, tasty-hunit, deepseq, parsec
+
+test-suite parsec-issue175
+    default-language: Haskell2010
+    type: exitcode-stdio-1.0
+    main-is: issue175.hs
+    hs-source-dirs: test
+    build-depends: base, tasty, tasty-hunit, parsec
diff --git a/src/Text/Parsec/Combinator.hs b/src/Text/Parsec/Combinator.hs
--- a/src/Text/Parsec/Combinator.hs
+++ b/src/Text/Parsec/Combinator.hs
@@ -106,24 +106,6 @@
                       scan  = do{ p; scan } <|> return ()
 -}
 
--- | @many1 p@ applies the parser @p@ /one/ or more times. Returns a
--- list of the returned values of @p@.
---
--- >  word  = many1 letter
-
-many1 :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m [a]
-{-# INLINABLE many1 #-}
-many1 p             = do{ x <- p; xs <- many p; return (x:xs) }
-{-
-many p              = scan id
-                    where
-                      scan f    = do{ x <- p
-                                    ; scan (\tail -> f (x:tail))
-                                    }
-                                <|> return (f [])
--}
-
-
 -- | @sepBy p sep@ parses /zero/ or more occurrences of @p@, separated
 -- by @sep@. Returns a list of values returned by @p@.
 --
diff --git a/src/Text/Parsec/Error.hs b/src/Text/Parsec/Error.hs
--- a/src/Text/Parsec/Error.hs
+++ b/src/Text/Parsec/Error.hs
@@ -25,8 +25,10 @@
     , mergeError
     ) where
 
+import Control.Exception ( Exception )
 import Data.List ( nub, sort )
 import Data.Typeable ( Typeable )
+import qualified Data.Monoid as Mon
 
 import Text.Parsec.Pos
 
@@ -145,12 +147,17 @@
     | null msgs2 && not (null msgs1) = e1
     | null msgs1 && not (null msgs2) = e2
     | otherwise
-    = case pos1 `compare` pos2 of
+      -- perfectly we'd compare the consumed token count
+      -- https://github.com/haskell/parsec/issues/175
+    = case compareErrorPos pos1 pos2 of
         -- select the longest match
         EQ -> ParseError pos1 (msgs1 ++ msgs2)
         GT -> e1
         LT -> e2
 
+compareErrorPos :: SourcePos -> SourcePos -> Ordering
+compareErrorPos x y = Mon.mappend (compare (sourceLine x) (sourceLine y)) (compare (sourceColumn x) (sourceColumn y))
+
 instance Show ParseError where
     show err
         = show (errorPos err) ++ ":" ++
@@ -163,6 +170,9 @@
         = errorPos l == errorPos r && messageStrs l == messageStrs r
         where
           messageStrs = map messageString . errorMessages
+
+-- | @since 3.1.17.0
+instance Exception ParseError
 
 -- Language independent show function
 
diff --git a/src/Text/Parsec/Prim.hs b/src/Text/Parsec/Prim.hs
--- a/src/Text/Parsec/Prim.hs
+++ b/src/Text/Parsec/Prim.hs
@@ -61,6 +61,7 @@
     , many
     , skipMany
     , manyAccum
+    , many1
     , runPT
     , runP
     , runParserT
@@ -270,6 +271,12 @@
     empty = mzero
     (<|>) = mplus
 
+    -- TODO: https://github.com/haskell/parsec/issues/179
+    -- investigate what's wrong with haddock
+    --
+    -- many = many
+    -- some = many1
+
 instance Monad (ParsecT s u m) where
     return = Applicative.pure
     p >>= f = parserBind p f
@@ -714,6 +721,15 @@
 many p
   = do xs <- manyAccum (:) p
        return (reverse xs)
+
+-- | @many1 p@ applies the parser @p@ /one/ or more times. Returns a
+-- list of the returned values of @p@.
+--
+-- >  word  = many1 letter
+
+many1 :: ParsecT s u m a -> ParsecT s u m [a]
+{-# INLINABLE many1 #-}
+many1 p = do{ x <- p; xs <- many p; return (x:xs) }
 
 -- | @skipMany p@ applies the parser @p@ /zero/ or more times, skipping
 -- its result.
diff --git a/test/issue127.hs b/test/issue127.hs
--- a/test/issue127.hs
+++ b/test/issue127.hs
@@ -1,3 +1,4 @@
+-- this should run in constant memory
 module Main (main) where
   
 import Text.Parsec
diff --git a/test/issue171.hs b/test/issue171.hs
new file mode 100644
--- /dev/null
+++ b/test/issue171.hs
@@ -0,0 +1,29 @@
+-- this should be fast
+module Main (main) where
+
+import Control.DeepSeq (NFData (..))
+import System.CPUTime (getCPUTime)
+import Text.Printf (printf)
+import Test.Tasty (defaultMain)
+import Test.Tasty.HUnit (testCaseSteps, assertBool)
+
+import Text.Parsec
+import Text.Parsec.String (Parser)
+
+main :: IO ()
+main = defaultMain $ testCaseSteps "issue-171" $ \info -> do
+  time0 <- getCPUTime
+  check $ concat $ replicate 100000 "a "
+  time1 <- getCPUTime
+  let diff = (time1 - time0) `div` 1000000000
+  info $ printf "%d milliseconds\n" diff
+  assertBool "" (diff < 200)
+
+parser :: Parser [String]
+parser = many (char 'a' <|> char 'b') `sepBy` char ' '
+
+check :: String -> IO ()
+check s = putStrLn $ either onError (const "") $ parse parser {- important: pass input as SourceName -} s s
+
+onError :: ParseError -> String
+onError err = rnf (show err) `seq` "error"
diff --git a/test/issue175.hs b/test/issue175.hs
new file mode 100644
--- /dev/null
+++ b/test/issue175.hs
@@ -0,0 +1,26 @@
+module Main (main) where
+
+import Text.Parsec
+import Text.Parsec.Error
+import Text.Parsec.String (Parser)
+import Text.Parsec.Pos (newPos)
+
+import Test.Tasty (defaultMain)
+import Test.Tasty.HUnit (assertFailure, testCaseSteps, (@?=))
+
+main :: IO ()
+main = defaultMain $ testCaseSteps "issue175" $ \info -> do
+    case parse p "" "x" of
+        Right _ -> assertFailure "Unexpected success"
+        -- with setPosition the "longest match" is arbitrary
+        -- megaparsec tracks consumed tokens separately, but we don't.
+        -- so our position is arbitrary.
+        Left err -> do
+            info $ show err
+            errorPos err @?= newPos "aaa" 9 1  -- can be arbitrary
+            length (errorMessages err) @?= 2
+
+p :: Parser Char
+p = p1 <|> p2 where
+    p1 = setPosition (newPos "aaa" 9 1) >> char 'a'
+    p2 = setPosition (newPos "zzz" 1 1) >> char 'b'
