diff --git a/src/Error/SerializableHttpException.php b/src/Error/SerializableHttpException.php
index a4ae814..de08a3e 100644
--- a/src/Error/SerializableHttpException.php
+++ b/src/Error/SerializableHttpException.php
@@ -3,10 +3,42 @@
 namespace Drupal\jsonapi\Error;
 
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
+use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Exception\HttpException;
 
 class SerializableHttpException extends HttpException {
 
   use DependencySerializationTrait;
 
+  /**
+   * @return array
+   */
+  public function serialize() {
+    $error = [];
+    $status_code = $this->getStatusCode();
+    if (!empty(Response::$statusTexts[$status_code])) {
+      $error['title'] = Response::$statusTexts[$status_code];
+    }
+    $error += [
+      'status' => $status_code,
+      'detail' => $this->getMessage(),
+      'code' => $this->getCode(),
+    ];
+
+    if (\Drupal::currentUser()->hasPermission('access site reports')) {
+      // The following information may contain sensitive information. Only show
+      // it to authorized users.
+      $error['source'] = [
+        'file' => $this->getFile(),
+        'line' => $this->getLine(),
+      ];
+      $error['meta'] = [
+        'exception' => (string) $this,
+        'trace' => $this->getTrace(),
+      ];
+    }
+
+    return $error;
+  }
+
 }
diff --git a/src/Error/UnprocessableEntityException.php b/src/Error/UnprocessableEntityException.php
new file mode 100644
index 0000000..0b7712e
--- /dev/null
+++ b/src/Error/UnprocessableEntityException.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Drupal\jsonapi\Error;
+
+use Drupal\Core\Entity\EntityConstraintViolationListInterface;
+use Symfony\Component\Validator\ConstraintViolationInterface;
+
+class UnprocessableEntityException extends SerializableHttpException {
+
+  /* @var EntityConstraintViolationListInterface */
+  protected $violations;
+
+  /**
+   * UnprocessableEntityException constructor.
+   *
+   * @param array $violations
+   * @param \Exception|NULL $previous
+   * @param array $headers
+   * @param int $code
+   */
+  public function __construct(EntityConstraintViolationListInterface $violations, \Exception $previous = null, array $headers = array(), $code = 0) {
+    parent::__construct(422, "Unprocessable Entity: validation failed.\n", $previous, $headers, $code);
+
+    $this->violations = $violations;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function serialize() {
+    /* @var ConstraintViolationInterface $violation */
+    foreach ($this->violations as $violation) {
+      $this->message .= $violation->getPropertyPath() . ': ' . $violation->getMessage() . "\n";
+    }
+
+    $error = parent::serialize();
+
+    if ($this->violations->count() == 1) {
+      // TODO: this will map a single-value field value to an array index of 0, even though the request will not use an array for that field. Fix this!
+      $pointer = '/data/attributes/' . str_replace('.', '/', $violation->getPropertyPath());
+    }
+    else {
+      $pointer = '/data';
+    }
+    $error['source']['pointer'] = $pointer;
+
+    return $error;
+  }
+
+}
diff --git a/src/Normalizer/HttpExceptionNormalizer.php b/src/Normalizer/HttpExceptionNormalizer.php
index 4fb9169..d4f7fd3 100644
--- a/src/Normalizer/HttpExceptionNormalizer.php
+++ b/src/Normalizer/HttpExceptionNormalizer.php
@@ -41,30 +41,42 @@ class HttpExceptionNormalizer extends SerializationNormalizerBase {
    */
   public function normalize($object, $format = null, array $context = []) {
     /** @var $object \Symfony\Component\HttpKernel\Exception\HttpException */
-    $error = [];
     $status_code = $object->getStatusCode();
-    if (!empty(Response::$statusTexts[$status_code])) {
-      $error['title'] = Response::$statusTexts[$status_code];
+    $error = [];
+
+    if (method_exists($object, 'serialize')) {
+      $error = $object->serialize();
     }
-    $error += [
-      'status' => $status_code,
-      'detail' => $object->getMessage(),
-      'links' => [
-        'info' => $this->getInfoUrl($status_code),
-      ],
-      'code' => $object->getCode(),
-    ];
-    if ($this->currentUser->hasPermission('access site reports')) {
-      // The following information may contain sensitive information. Only show
-      // it to authorized users.
-      $error['source'] = [
-        'file' => $object->getFile(),
-        'line' => $object->getLine(),
-      ];
-      $error['meta'] = [
-        'exception' => (string) $object,
-        'trace' => $object->getTrace(),
+    else {
+      // TODO: can this be removed? (can we assume $object is a SerializableHttpException?)
+      if (!empty(Response::$statusTexts[$status_code])) {
+        $error['title'] = Response::$statusTexts[$status_code];
+      }
+      $error += [
+        'status' => $status_code,
+        'detail' => $object->getMessage(),
+        // TODO: Use a non-specific source pointer? :
+        /*'source' => [
+          'pointer' => '/data/attributes/title',
+        ],*/
+        'code' => $object->getCode(),
       ];
+      if ($this->currentUser->hasPermission('access site reports')) {
+        // The following information may contain sensitive information. Only show
+        // it to authorized users.
+        $error['source'] = [
+          'file' => $object->getFile(),
+          'line' => $object->getLine(),
+        ];
+        $error['meta'] = [
+          'exception' => (string) $object,
+          'trace' => $object->getTrace(),
+        ];
+      }
+    }
+
+    if (empty($error['links']['info'])) {
+      $error['links']['info'] = $this->getInfoUrl($status_code);
     }
 
     return new HttpExceptionNormalizerValue(
diff --git a/src/Resource/EntityResource.php b/src/Resource/EntityResource.php
index fd62d0f..5f87c30 100644
--- a/src/Resource/EntityResource.php
+++ b/src/Resource/EntityResource.php
@@ -18,6 +18,7 @@ use Drupal\jsonapi\Configuration\ResourceConfigInterface;
 use Drupal\jsonapi\EntityCollection;
 use Drupal\jsonapi\EntityCollectionInterface;
 use Drupal\jsonapi\Error\SerializableHttpException;
+use Drupal\jsonapi\Error\UnprocessableEntityException;
 use Drupal\jsonapi\Query\QueryBuilderInterface;
 use Drupal\jsonapi\Context\CurrentContextInterface;
 use Drupal\jsonapi\ResourceResponse;
@@ -133,15 +134,11 @@ class EntityResource implements EntityResourceInterface {
     $violations->filterByFieldAccess();
 
     if (count($violations) > 0) {
-      $message = "Unprocessable Entity: validation failed.\n";
-      foreach ($violations as $violation) {
-        $message .= $violation->getPropertyPath() . ': ' . $violation->getMessage() . "\n";
-      }
       // Instead of returning a generic 400 response we use the more specific
       // 422 Unprocessable Entity code from RFC 4918. That way clients can
       // distinguish between general syntax errors in bad serializations (code
       // 400) and semantic errors in well-formed requests (code 422).
-      throw new SerializableHttpException(422, $message);
+      throw new UnprocessableEntityException($violations);
     }
   }
 
