diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,2 +0,0 @@
-import Distribution.Simple
-main = defaultMain
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,16 @@
+# 0.4.2
+
+* BREAKING CHANGE: `FromJSONError` now carries the failing `Value` as payload.
+
+* BREAKING CHANGE: Removed support for `ErrorT`.
+
+* Added `loop` and `loopL`.
+
+* GHC >= 9.0 compatibility.
+
+* Add dependency on `attoparsec-aeson`.
+
+
 # 0.4.1.8
 
 * Remove upper bound dependencies on everything but `base`.
diff --git a/pipes-aeson.cabal b/pipes-aeson.cabal
--- a/pipes-aeson.cabal
+++ b/pipes-aeson.cabal
@@ -1,17 +1,16 @@
+cabal-version:      2.4
 name:               pipes-aeson
-version:            0.4.1.8
-license:            BSD3
+version:            0.4.2
+license:            BSD-3-Clause
 license-file:       LICENSE
-copyright:          Copyright (c) Renzo Carbonara 2013-2017
+copyright:          Copyright (c) Renzo Carbonara 2013
 author:             Renzo Carbonara
-maintainer:         renzocarbonaraλgmail.com
+maintainer:         renλren.zone
 stability:          Experimental
-tested-with:        GHC ==8.0.2
 homepage:           https://github.com/k0001/pipes-aeson
 bug-reports:        https://github.com/k0001/pipes-aeson/issues
 category:           Pipes, Parser
 build-type:         Simple
-cabal-version:      >=1.8
 synopsis:           Encode and decode JSON streams using Aeson and Pipes.
 extra-source-files: README.md PEOPLE changelog.md
 description:
@@ -25,6 +24,7 @@
     location: git://github.com/k0001/pipes-aeson.git
 
 library
+  default-language: Haskell2010
   hs-source-dirs:  src
   exposed-modules: Pipes.Aeson
                    Pipes.Aeson.Unchecked
@@ -32,6 +32,7 @@
   build-depends:
       aeson            (>=0.6.1)
     , attoparsec       (>=0.10)
+    , attoparsec-aeson (>=2.2)
     , base             (>=4.5 && <5.0)
     , pipes            (>=4.1)
     , pipes-attoparsec (>=0.5)
diff --git a/src/Pipes/Aeson.hs b/src/Pipes/Aeson.hs
--- a/src/Pipes/Aeson.hs
+++ b/src/Pipes/Aeson.hs
@@ -24,16 +24,18 @@
     -- $decoding
   , decode
   , decoded
+  , loop
     -- ** Including lengths
   , decodeL
   , decodedL
+  , loopL
 
     -- * Types
   , I.DecodingError(..)
   ) where
 
-import           Control.Monad         (liftM)
 import qualified Data.Aeson            as Ae
+import qualified Data.Aeson.Parser     as Ae
 import qualified Data.ByteString.Char8 as B
 import           Pipes
 import qualified Pipes.Aeson.Internal  as I
@@ -60,7 +62,7 @@
 -- @
 -- 'for' 'cat' 'encodeObject' :: 'Monad' m => 'Pipe' 'Ae.Object' 'B.ByteString' m r
 -- @
-encodeObject :: Monad m => Ae.Object -> Producer' B.ByteString m ()
+encodeObject :: Monad m => Ae.Object -> Proxy x' x () B.ByteString m ()
 encodeObject = U.encode
 {-# INLINABLE encodeObject #-}
 {-# RULES "p >-> for cat encodeObject" forall p .
@@ -75,7 +77,7 @@
 -- @
 -- 'for' 'cat' 'encodeArray' :: 'Monad' m => 'Pipe' 'Ae.Array' 'B.ByteString' m r
 -- @
-encodeArray :: Monad m => Ae.Array -> Producer' B.ByteString m ()
+encodeArray :: Monad m => Ae.Array -> Proxy x' x () B.ByteString m ()
 encodeArray = U.encode
 {-# INLINABLE encodeArray #-}
 {-# RULES "p >-> for cat encodeArray" forall p .
@@ -108,7 +110,7 @@
 decode
   :: (Monad m, Ae.FromJSON a)
   => Pipes.Parser B.ByteString m (Maybe (Either I.DecodingError a))
-decode = fmap (fmap snd) `liftM` decodeL
+decode = fmap (fmap (fmap snd)) decodeL
 {-# INLINABLE decode #-}
 
 -- | Like 'decode', except it also returns the length of JSON input that was
@@ -172,6 +174,61 @@
          Right r      -> return r
 {-# INLINABLE decodedL #-}
 
+-- | Repeteadly try to parse raw JSON bytes into @a@ values, reporting any
+-- 'I.DecodingError's downstream as they happen.
+--
+-- /Note:/ The JSON RFC-4627 standard only allows arrays or objects as top-level
+-- entities, which is why these functions restrict their input to them. If you
+-- prefer to ignore the standard and encode any 'Ae.Value', then use 'U.encode'
+-- from the "Pipes.Aeson.Unchecked" module.
+loop
+  :: (Monad m, Ae.FromJSON a)
+  => (Pipes.Producer B.ByteString m r -> Pipes.Producer B.ByteString m r)
+  -- ^ In case of 'I.AttoparsecError', this function will be called to modify
+  -- the leftovers 'Pipes.Producer' before using it.
+  --
+  -- Ideally you will want to drop everything until the beginning of the next
+  -- JSON element. This is easy to accomplish if there is a clear whitespace
+  -- delimiter between the JSON elements, such as a newline (i.e.,
+  -- @'Pipes.ByteString.drop' 1 . 'Pipes.ByteString.dropWhile' (/= 0xA)@).
+  -- However, it can be hard to do correctly is there is no such delimiter.
+  -- Skipping the first character (i.e., @'Pipes.ByteString.drop' 1@) should be
+  -- sufficient in most cases, but not when parsing recursive data structures
+  -- because you can accidentally parse a child in its parent's stead.
+  --
+  -- Notice that unless you advance the 'Pipes.Producer' somehow, 'loop'
+  -- will never terminate.
+  -> Pipes.Producer B.ByteString m r
+  -- ^ Raw JSON input.
+  -> Pipes.Producer' (Either I.DecodingError a) m r
+{-# INLINABLE loop #-}
+loop fp p0 = Pipes.for (loopL fp p0) (Pipes.yield . fmap snd)
+
+-- | Like 'loop', except it also outputs the length of JSON input that was
+-- consumed in order to obtain the value, not including the length of whitespace
+-- before nor after the parsed JSON input.
+loopL
+  :: (Monad m, Ae.FromJSON a)
+  => (Pipes.Producer B.ByteString m r -> Pipes.Producer B.ByteString m r)
+  -- ^ In case of 'I.AttoparsecError', this function will be called to modify
+  -- the leftovers 'Pipes.Producer' before using it.
+  --
+  -- Ideally you will want to drop everything until the beginning of the next
+  -- JSON element. This is easy to accomplish if there is a clear whitespace
+  -- delimiter between the JSON elements, such as a newline (i.e.,
+  -- @'Pipes.ByteString.drop' 1 . 'Pipes.ByteString.dropWhile' (/= 0xA)@).
+  -- However, it can be hard to do correctly is there is no such delimiter.
+  -- Skipping the first character (i.e., @'Pipes.ByteString.drop' 1@) should be
+  -- sufficient in most cases, but not when parsing recursive data structures
+  -- because you can accidentally parse a child in its parent's stead.
+  --
+  -- Notice that unless you advance the 'Pipes.Producer' somehow, 'loopL'
+  -- will never terminate.
+  -> Pipes.Producer B.ByteString m r
+  -- ^ Raw JSON input.
+  -> Pipes.Proxy x' x () (Either I.DecodingError (Int, a)) m r
+{-# INLINABLE loopL #-}
+loopL = I.loopL Ae.json'
 
 --------------------------------------------------------------------------------
 -- Internal tools --------------------------------------------------------------
diff --git a/src/Pipes/Aeson/Internal.hs b/src/Pipes/Aeson/Internal.hs
--- a/src/Pipes/Aeson/Internal.hs
+++ b/src/Pipes/Aeson/Internal.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE FlexibleInstances  #-}
+{-# LANGUAGE LambdaCase         #-}
 {-# LANGUAGE RankNTypes         #-}
 
 -- | This module provides internal utilities and it is likely
@@ -10,15 +11,16 @@
   ( DecodingError(..)
   , consecutively
   , decodeL
+  , loopL
   ) where
 import           Control.Exception                (Exception)
-import           Control.Monad.Trans.Error        (Error)
 import qualified Control.Monad.Trans.State.Strict as S
 import qualified Data.Aeson                       as Ae
 import qualified Data.Attoparsec.Types            as Attoparsec
 import qualified Data.ByteString                  as B
 import qualified Data.ByteString.Internal         as B (isSpaceWord8)
 import           Data.Data                        (Data, Typeable)
+import           Data.Function                    (fix)
 import           Pipes
 import qualified Pipes.Attoparsec                 as PA
 import qualified Pipes.Parse                      as Pipes
@@ -29,19 +31,14 @@
 data DecodingError
   = AttoparsecError PA.ParsingError
     -- ^An @attoparsec@ error that happened while parsing the raw JSON string.
-  | FromJSONError String
-    -- ^An @aeson@ error that happened while trying to convert a
-    -- 'Data.Aeson.Value' to an 'A.FromJSON' instance, as reported by
+  | FromJSONError Ae.Value String
+    -- ^An @aeson@ error that happened while trying to convert the given
+    -- 'Data.Aeson.Value' to an 'Ae.FromJSON' instance, as reported by
     -- 'Data.Aeson.Error'.
   deriving (Show, Eq, Data, Typeable)
 
 instance Exception DecodingError
-instance Error     DecodingError
 
--- | This instance allows using 'Pipes.Lift.errorP' with 'Pipes.Aeson.decoded'
--- and 'Pipes.Aeson.decodedL'
-instance Error (DecodingError, Producer a m r)
-
 --------------------------------------------------------------------------------
 
 -- | Consecutively parse 'a' elements from the given 'Producer' using the given
@@ -50,11 +47,11 @@
 --
 -- This 'Producer' runs until it either runs out of input or until a decoding
 -- failure occurs, in which case it returns 'Left' with a 'DecodingError' and
--- a 'Producer' with any leftovers. You can use 'Pipes.Lift.errorP' to turn the
--- 'Either' return value into an 'Control.Monad.Trans.Error.ErrorT'
+-- a 'Producer' with any leftovers. You can use 'Pipes.Lift.exceptP' to turn the
+-- 'Either' return value into an 'Control.Monad.Trans.ExceptT'
 -- monad transformer.
 consecutively
-  :: (Monad m)
+  :: Monad m
   => Pipes.Parser B.ByteString m (Maybe (Either e a))
   -> Producer B.ByteString m r  -- ^Producer from which to draw raw input.
   -> Producer a m (Either (e, Producer B.ByteString m r) r)
@@ -87,10 +84,46 @@
        Nothing             -> Nothing
        Just (Left l)       -> Just (Left (AttoparsecError l))
        Just (Right (n, v)) -> case Ae.fromJSON v of
-          Ae.Error e   -> Just (Left (FromJSONError e))
+          Ae.Error e   -> Just (Left (FromJSONError v e))
           Ae.Success a -> Just (Right (n, a))
 {-# INLINABLE decodeL #-}
 
+--------------------------------------------------------------------------------
+
+-- | Repeteadly try to parse raw JSON bytes into @a@ values, reporting any
+-- 'I.DecodingError's downstream as they happen.
+loopL
+  :: (Monad m, Ae.FromJSON a)
+  => Attoparsec.Parser B.ByteString Ae.Value
+  -> (Pipes.Producer B.ByteString m r -> Pipes.Producer B.ByteString m r)
+  -- ^ In case of 'AttoparsecError', this function will be called to modify
+  -- the leftovers 'Pipes.Producer' before using it.
+  --
+  -- Ideally you will want to drop everything until the beginning of the next
+  -- JSON element. This is easy to accomplish if there is a clear whitespace
+  -- delimiter between the JSON elements, such as a newline (i.e.,
+  -- @'Pipes.ByteString.drop' 1 . 'Pipes.ByteString.dropWhile' (/= 0xA)@).
+  -- However, it can be hard to do correctly is there is no such delimiter.
+  -- Skipping the first character (i.e., @'Pipes.ByteString.drop' 1@) should be
+  -- sufficient in most cases, but not when parsing recursive data structures
+  -- because you can accidentally parse a child in its parent's stead.
+  --
+  -- Notice that unless you advance the 'Pipes.Producer' somehow, 'loopL' will
+  -- never terminate.
+  -> Pipes.Producer B.ByteString m r
+  -- ^ Raw JSON input.
+  -> Pipes.Proxy x' x () (Either DecodingError (Int, a)) m r
+{-# INLINABLE loopL #-}
+loopL parser fp = fix $ \k p0 -> do
+   (ye, p1) <- lift (S.runStateT (decodeL parser) p0)
+   case ye of
+      Just (Left e@AttoparsecError{}) -> do
+         Pipes.yield (Left e)
+         k (fp p1)
+      Just ea -> Pipes.yield ea >> k p1
+      Nothing -> lift (Pipes.next p1) >>= \case
+         Left r -> pure r
+         Right _ -> error "Pipes.Aeson.Internal.loopL: impossible"
 
 --------------------------------------------------------------------------------
 -- Internal stuff
diff --git a/src/Pipes/Aeson/Unchecked.hs b/src/Pipes/Aeson/Unchecked.hs
--- a/src/Pipes/Aeson/Unchecked.hs
+++ b/src/Pipes/Aeson/Unchecked.hs
@@ -12,9 +12,11 @@
     -- * Decoding
   , decode
   , decoded
+  , loop
     -- ** Including lenghts
   , decodeL
   , decodedL
+  , loopL
   ) where
 
 import           Control.Monad        (liftM)
@@ -30,8 +32,9 @@
 
 -- | Like 'Pipes.Aeson.encode', except it accepts any 'Ae.ToJSON' instance,
 -- not just 'Ae.Array' or 'Ae.Object'.
-encode :: (Monad m, Ae.ToJSON a) => a -> Producer' B.ByteString m ()
-encode = PB.fromLazy . Ae.encode
+-- encode :: (Monad m, Ae.ToJSON a) => a -> Producer' B.ByteString m ()
+encode :: (Monad m, Ae.ToJSON a) => a -> Proxy x' x () PB.ByteString m ()
+encode a = PB.fromLazy (Ae.encode a)
 {-# INLINABLE encode #-}
 {-# RULES "p >-> for cat encode" forall p .
     p >-> for cat encode = for p (\a -> PB.fromLazy (Ae.encode a))
@@ -93,6 +96,57 @@
     {-# INLINE _encode #-}
 {-# INLINABLE decodedL #-}
 
+
+-- | Repeteadly try to parse raw JSON bytes into @a@ values, reporting any
+-- 'I.DecodingError's downstream as they happen.
+loop
+  :: (Monad m, Ae.FromJSON a)
+  => (Pipes.Producer B.ByteString m r -> Pipes.Producer B.ByteString m r)
+  -- ^ In case of 'I.AttoparsecError', this function will be called to modify
+  -- the leftovers 'Pipes.Producer' before using it.
+  --
+  -- Ideally you will want to drop everything until the beginning of the next
+  -- JSON element. This is easy to accomplish if there is a clear whitespace
+  -- delimiter between the JSON elements, such as a newline (i.e.,
+  -- @'Pipes.ByteString.drop' 1 . 'Pipes.ByteString.dropWhile' (/= 0xA)@).
+  -- However, it can be hard to do correctly is there is no such delimiter.
+  -- Skipping the first character (i.e., @'Pipes.ByteString.drop' 1@) should be
+  -- sufficient in most cases, but not when parsing recursive data structures
+  -- because you can accidentally parse a child in its parent's stead.
+  --
+  -- Notice that unless you advance the 'Pipes.Producer' somehow, 'loop'
+  -- will never terminate.
+  -> Pipes.Producer B.ByteString m r
+  -- ^ Raw JSON input.
+  -> Pipes.Producer' (Either I.DecodingError a) m r
+{-# INLINABLE loop #-}
+loop fp p0 = Pipes.for (loopL fp p0) (Pipes.yield . fmap snd)
+
+-- | Like 'loop', except it also outputs the length of JSON input that was
+-- consumed in order to obtain the value, not including the length of whitespace
+-- before nor after the parsed JSON input.
+loopL
+  :: (Monad m, Ae.FromJSON a)
+  => (Pipes.Producer B.ByteString m r -> Pipes.Producer B.ByteString m r)
+  -- ^ In case of 'I.AttoparsecError', this function will be called to modify
+  -- the leftovers 'Pipes.Producer' before using it.
+  --
+  -- Ideally you will want to drop everything until the beginning of the next
+  -- JSON element. This is easy to accomplish if there is a clear whitespace
+  -- delimiter between the JSON elements, such as a newline (i.e.,
+  -- @'Pipes.ByteString.drop' 1 . 'Pipes.ByteString.dropWhile' (/= 0xA)@).
+  -- However, it can be hard to do correctly is there is no such delimiter.
+  -- Skipping the first character (i.e., @'Pipes.ByteString.drop' 1@) should be
+  -- sufficient in most cases, but not when parsing recursive data structures
+  -- because you can accidentally parse a child in its parent's stead.
+  --
+  -- Notice that unless you advance the 'Pipes.Producer' somehow, 'loopL'
+  -- will never terminate.
+  -> Pipes.Producer B.ByteString m r
+  -- ^ Raw JSON input.
+  -> Pipes.Proxy x' x () (Either I.DecodingError (Int, a)) m r
+{-# INLINABLE loopL #-}
+loopL = I.loopL Ae.value'
 
 --------------------------------------------------------------------------------
 -- Internal tools --------------------------------------------------------------
