diff --git a/Yesod/Json.hs b/Yesod/Json.hs
--- a/Yesod/Json.hs
+++ b/Yesod/Json.hs
@@ -45,7 +45,7 @@
 import Data.Text.Lazy.Encoding (decodeUtf8)
 import Data.Text.Lazy.Builder (toLazyText)
 import qualified Blaze.ByteString.Builder.Char.Utf8 as Blaze
-import Data.Conduit (($$))
+import Data.Conduit
 import Network.Wai (requestBody, requestHeaders)
 import Network.Wai.Parse (parseHttpAccept)
 import qualified Data.ByteString.Char8 as B8
@@ -88,7 +88,13 @@
 parseJsonBody :: J.FromJSON a => GHandler sub master (J.Result a)
 parseJsonBody = do
     req <- waiRequest
-    fmap J.fromJSON $ lift $ requestBody req $$ sinkParser JP.value'
+    eValue <- lift
+            $ runExceptionT
+            $ transPipe lift (requestBody req)
+           $$ sinkParser JP.value'
+    return $ case eValue of
+        Left e -> J.Error $ show e
+        Right value -> J.fromJSON value
 
 -- | Same as 'parseJsonBody', but return an invalid args response on a parse
 -- error.
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,47 @@
+{-# LANGUAGE OverloadedStrings, TemplateHaskell, QuasiQuotes, TypeFamilies, MultiParamTypeClasses #-}
+import Yesod.Core
+import Yesod.Json
+import Test.Hspec
+import qualified Data.Map as Map
+import Network.Wai.Test
+import Data.Text (Text)
+
+data App = App
+
+mkYesod "App" [parseRoutes|
+/ HomeR GET
+|]
+
+instance Yesod App
+
+getHomeR :: Handler RepPlain
+getHomeR = do
+    val <- parseJsonBody_
+    case Map.lookup ("foo" :: Text) val of
+        Nothing -> invalidArgs ["foo not found"]
+        Just foo -> return $ RepPlain $ toContent (foo :: Text)
+
+main :: IO ()
+main = do
+    app <- toWaiApp App
+    hspec $ describe "Yesod.Json" $ do
+        it "parses valid content" $ flip runSession app $ do
+            sres <- srequest SRequest
+                { simpleRequest = defaultRequest
+                , simpleRequestBody = "{\"foo\":\"bar\"}"
+                }
+            assertStatus 200 sres
+            assertBody "bar" sres
+        it "400 for bad JSON" $ flip runSession app $ do
+            sres <- srequest SRequest
+                { simpleRequest = defaultRequest
+                , simpleRequestBody = "{\"foo\":\"bar\""
+                }
+            assertStatus 400 sres
+        it "400 for bad structure" $ flip runSession app $ do
+            sres <- srequest SRequest
+                { simpleRequest = defaultRequest
+                , simpleRequestBody = "{\"foo2\":\"bar\"}"
+                }
+            assertStatus 400 sres
+            assertBodyContains "foo not found" sres
diff --git a/yesod-json.cabal b/yesod-json.cabal
--- a/yesod-json.cabal
+++ b/yesod-json.cabal
@@ -1,5 +1,5 @@
 name:            yesod-json
-version:         1.1.2.1
+version:         1.1.2.2
 license:         MIT
 license-file:    LICENSE
 author:          Michael Snoyman <michael@snoyman.com>
@@ -7,7 +7,7 @@
 synopsis:        Generate content for Yesod using the aeson package.
 category:        Web, Yesod
 stability:       Stable
-cabal-version:   >= 1.6
+cabal-version:   >= 1.8
 build-type:      Simple
 homepage:        http://www.yesodweb.com/
 description:     Generate content for Yesod using the aeson package.
@@ -31,6 +31,18 @@
                    , safe                  >= 0.2
     exposed-modules: Yesod.Json
     ghc-options:     -Wall
+
+test-suite tests
+    type: exitcode-stdio-1.0
+    main-is: Spec.hs
+    hs-source-dirs: test
+    build-depends: base
+                 , wai-test
+                 , hspec
+                 , yesod-json
+                 , yesod-core
+                 , text
+                 , containers
 
 source-repository head
   type:     git
