diff --git a/Data/Aeson/Types.hs b/Data/Aeson/Types.hs
--- a/Data/Aeson/Types.hs
+++ b/Data/Aeson/Types.hs
@@ -141,6 +141,8 @@
     , (<?>)
     , JSONPath
     , JSONPathElement(..)
+    , formatPath
+    , formatRelativePath
     ) where
 
 import Prelude.Compat
diff --git a/Data/Aeson/Types/FromJSON.hs b/Data/Aeson/Types/FromJSON.hs
--- a/Data/Aeson/Types/FromJSON.hs
+++ b/Data/Aeson/Types/FromJSON.hs
@@ -892,7 +892,7 @@
 -- E.g. @'explicitParseField' 'parseJSON1' :: ('FromJSON1' f, 'FromJSON' a) -> 'Object' -> 'Text' -> 'Parser' (f a)@
 explicitParseField :: (Value -> Parser a) -> Object -> Text -> Parser a
 explicitParseField p obj key = case H.lookup key obj of
-    Nothing -> fail $ "key " ++ show key ++ " not present"
+    Nothing -> fail $ "key " ++ show key ++ " not found"
     Just v  -> p v <?> Key key
 {-# INLINE explicitParseField #-}
 
@@ -951,6 +951,16 @@
 contextType :: TypeName -> Parser a -> Parser a
 contextType = prependContext
 
+-- | Add the tagKey that will be looked up while building an ADT
+-- | Produce the error equivalent to
+-- | Left "Error in $: parsing T failed, expected an object with keys "tag" and
+-- | "contents", where "tag" i-- |s associated to one of ["Foo", "Bar"],
+-- | The parser returned error was: could not find key "tag"
+contextTag :: Text -> [String] -> Parser a -> Parser a
+contextTag tagKey cnames = prependFailure
+  ("expected Object with key \"" ++ unpack tagKey ++ "\"" ++
+  " containing one of " ++ show cnames ++ ", ")
+
 -- | Add the name of the constructor being parsed to a parser's error messages.
 contextCons :: ConName -> TypeName -> Parser a -> Parser a
 contextCons cname tname = prependContext (showCons cname tname)
@@ -1129,8 +1139,8 @@
         retag (Tagged2 x) = Tagged2 x
 
 --------------------------------------------------------------------------------
-
-parseNonAllNullarySum :: ( FromPair          arity f
+parseNonAllNullarySum :: forall f c arity.
+                         ( FromPair          arity f
                          , FromTaggedObject  arity f
                          , FromUntaggedValue arity f
                          , ConstructorNames        f
@@ -1140,7 +1150,7 @@
     case sumEncoding opts of
       TaggedObject{..} ->
           withObject tname $ \obj -> do
-              tag <- contextType tname $ obj .: tagKey
+              tag <- contextType tname . contextTag tagKey cnames_ $ obj .: tagKey
               fromMaybe (badTag tag <?> Key tagKey) $
                   parseFromTaggedObject (tag :* contentsFieldName :* p) obj
         where
@@ -1148,6 +1158,7 @@
           badTag tag = failWith_ $ \cnames ->
               "expected tag field to be one of " ++ show cnames ++
               ", but found tag " ++ show tag
+          cnames_ = unTagged2 (constructorTags (constructorTagModifier opts) :: Tagged2 f [String])
 
       ObjectWithSingleField ->
           withObject tname $ \obj -> case H.toList obj of
diff --git a/Data/Aeson/Types/Internal.hs b/Data/Aeson/Types/Internal.hs
--- a/Data/Aeson/Types/Internal.hs
+++ b/Data/Aeson/Types/Internal.hs
@@ -49,6 +49,8 @@
     , parserThrowError
     , parserCatchError
     , formatError
+    , formatPath
+    , formatRelativePath
     , (<?>)
     -- * Constructors and accessors
     , object
@@ -458,7 +460,17 @@
 -- | Annotate an error message with a
 -- <http://goessner.net/articles/JsonPath/ JSONPath> error location.
 formatError :: JSONPath -> String -> String
-formatError path msg = "Error in " ++ format "$" path ++ ": " ++ msg
+formatError path msg = "Error in " ++ formatPath path ++ ": " ++ msg
+
+-- | Format a <http://goessner.net/articles/JsonPath/ JSONPath> as a 'String',
+-- representing the root object as @$@.
+formatPath :: JSONPath -> String
+formatPath path = "$" ++ formatRelativePath path
+
+-- | Format a <http://goessner.net/articles/JsonPath/ JSONPath> as a 'String'
+-- which represents the path relative to some root object.
+formatRelativePath :: JSONPath -> String
+formatRelativePath path = format "" path
   where
     format :: String -> JSONPath -> String
     format pfx []                = pfx
diff --git a/aeson.cabal b/aeson.cabal
--- a/aeson.cabal
+++ b/aeson.cabal
@@ -1,5 +1,5 @@
 name:            aeson
-version:         1.4.5.0
+version:         1.4.6.0
 license:         BSD3
 license-file:    LICENSE
 category:        Text, Web, JSON
@@ -225,7 +225,7 @@
     containers,
     directory,
     dlist,
-    Diff,
+    Diff >= 0.4 && < 0.5,
     filepath,
     generic-deriving >= 1.10 && < 1.13,
     ghc-prim >= 0.2,
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,13 @@
 For the latest version of this document, please see [https://github.com/bos/aeson/blob/master/changelog.md](https://github.com/bos/aeson/blob/master/changelog.md).
 
+### 1.4.6.0
+
+* Provide a clearer error message when a required tagKey for a constructor is missing, thanks to Guru Devanla.
+  The error message now looks like this: `Error in $: parsing Types.SomeType failed, expected Object with key "tag" containing one of ["nullary","unary","product","record","list"], key "tag" not found`
+
+* Add `formatPath` and `formatRelativePath` functions to turn a `JSONPath` into a `String`, thanks to Robbie McMichael
+
+
 ### 1.4.5.0
 
 * Expose `(<?>)`, `JSONPath` and `JSONPathElement(..)` from `Data.Aeson.Types`. Previously only available through internal modules. Thanks to Luke Clifton.
diff --git a/stack-bench.yaml b/stack-bench.yaml
--- a/stack-bench.yaml
+++ b/stack-bench.yaml
@@ -1,4 +1,4 @@
-resolver: lts-12.26
+resolver: nightly-2019-09-21
 # We use aeson in the snapshot to
 # - avoid recompilation of criterion
 # - compare against it
@@ -9,10 +9,4 @@
 work-dir: .stack-work-bench
 packages:
 - benchmarks
-extra-deps:
-- base-orphans-0.8.1
-- hashable-time-0.2.0.1
-- QuickCheck-2.13.1
-- quickcheck-instances-0.3.21
-- splitmix-0.0.2
-- time-compat-1.9.2.2
+extra-deps: []
diff --git a/stack-ffi-unescape.yaml b/stack-ffi-unescape.yaml
--- a/stack-ffi-unescape.yaml
+++ b/stack-ffi-unescape.yaml
@@ -1,4 +1,4 @@
-resolver: lts-12.26
+resolver: nightly-2019-09-21
 packages:
 - '.'
 flags:
@@ -6,9 +6,4 @@
     fast: true
     cffi: true
 extra-deps:
-- base-orphans-0.8.1
-- hashable-time-0.2.0.1
-- QuickCheck-2.13.1
-- quickcheck-instances-0.3.21
-- splitmix-0.0.2
-- time-compat-1.9.2.2
+- Diff-0.4.0
diff --git a/stack-nightly.yaml b/stack-nightly.yaml
--- a/stack-nightly.yaml
+++ b/stack-nightly.yaml
@@ -1,4 +1,4 @@
-resolver: nightly-2019-07-11
+resolver: nightly-2019-10-14
 packages:
 - '.'
 - attoparsec-iso8601
@@ -8,6 +8,4 @@
   attoparsec-iso8601:
     fast: true
 extra-deps:
-- hlint-2.1.18
-- time-compat-1.9.2.2
-- quickcheck-instances-0.3.21
+- generic-deriving-1.12.4
diff --git a/tests/ErrorMessages.hs b/tests/ErrorMessages.hs
--- a/tests/ErrorMessages.hs
+++ b/tests/ErrorMessages.hs
@@ -13,7 +13,7 @@
 import Data.Aeson.Types (Parser)
 import Data.Aeson.Parser (eitherDecodeWith)
 import Data.Aeson.Internal (formatError, iparse)
-import Data.Algorithm.Diff (Diff(..), getGroupedDiff)
+import Data.Algorithm.Diff (PolyDiff (..), getGroupedDiff)
 import Data.Proxy (Proxy(..))
 import Data.Semigroup ((<>))
 import Data.Sequence (Seq)
diff --git a/tests/UnitTests.hs b/tests/UnitTests.hs
--- a/tests/UnitTests.hs
+++ b/tests/UnitTests.hs
@@ -34,7 +34,8 @@
   , json', jsonLast', jsonAccum', jsonNoDup')
 import Data.Aeson.Types
   ( Options(..), Result(Success), ToJSON(..), Value(Array, Bool, Null, Object)
-  , camelTo, camelTo2, defaultOptions, omitNothingFields, parse)
+  , camelTo, camelTo2, defaultOptions, formatPath, formatRelativePath
+  , omitNothingFields, parse)
 import Data.Attoparsec.ByteString (Parser, parseOnly)
 import Data.Char (toUpper)
 import Data.Either.Compat (isLeft, isRight)
@@ -252,6 +253,18 @@
   let rhs = formatError [Index 0, Key "foo", Key "bar", Key "a.b.c", Key "", Key "'\\", Key "end"] "error msg"
       lhs = "Error in $[0].foo.bar['a.b.c']['']['\\'\\\\'].end: error msg"
   in assertEqual "formatError example" lhs rhs
+
+formatPathExample :: Assertion
+formatPathExample =
+  let rhs = formatPath [Key "x", Index 0]
+      lhs = "$.x[0]"
+  in assertEqual "formatPath example" lhs rhs
+
+formatRelativePathExample :: Assertion
+formatRelativePathExample =
+  let rhs = formatPath [Key "x", Index 0]
+      lhs = ".x[0]"
+  in assertEqual "formatRelativePath example" lhs rhs
 
 ------------------------------------------------------------------------------
 -- Comparison (.:?) and (.:!)
diff --git a/tests/golden/generic.expected b/tests/golden/generic.expected
--- a/tests/golden/generic.expected
+++ b/tests/golden/generic.expected
@@ -6,11 +6,11 @@
 Error in $: parsing Types.Nullary failed, expected String, but encountered Array
 SomeType (tagged)
 Error in $.contents: parsing Int failed, expected Number, but encountered Boolean
-Error in $: parsing Types.SomeType(Unary) failed, key "contents" not present
-Error in $: parsing Types.SomeType(Record) failed, key "testone" not present
+Error in $: parsing Types.SomeType(Unary) failed, key "contents" not found
+Error in $: parsing Types.SomeType(Record) failed, key "testone" not found
 Error in $.testone: parsing Double failed, unexpected Boolean
 Error in $.tag: parsing Types.SomeType failed, expected tag field to be one of ["nullary","unary","product","record","list"], but found tag "X"
-Error in $: parsing Types.SomeType failed, key "tag" not present
+Error in $: parsing Types.SomeType failed, expected Object with key "tag" containing one of ["nullary","unary","product","record","list"], key "tag" not found
 Error in $: parsing Types.SomeType failed, expected Object, but encountered Array
 SomeType (single-field)
 Error in $.unary: parsing Int failed, expected Number, but encountered Object
