diff --git a/core/includes/common.inc b/core/includes/common.inc
index 08ce2ab..55661d9 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -7031,6 +7031,9 @@ function drupal_common_theme() {
     'form' => array(
       'render element' => 'element',
     ),
+    'form_error_message' => array(
+      'render element' => 'element',
+    ),
     'textarea' => array(
       'render element' => 'element',
     ),
diff --git a/core/includes/form.inc b/core/includes/form.inc
index b5a13c5..e934c80 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -858,7 +858,7 @@ function drupal_process_form($form_id, &$form, &$form_state) {
     // browser don't increment all the element IDs needlessly.
     drupal_static_reset('drupal_html_id');
 
-    if ($form_state['submitted'] && !form_get_errors() && !$form_state['rebuild']) {
+    if ($form_state['submitted'] && !$form_state['rebuild'] && !form_get_errors()) {
       // Execute form submit handlers.
       form_execute_handlers('submit', $form, $form_state);
 
@@ -902,9 +902,16 @@ function drupal_process_form($form_id, &$form, &$form_state) {
       // Set a flag to indicate the the form has been processed and executed.
       $form_state['executed'] = TRUE;
 
+      // Occassionally a form will set an error in its submit handler so we make
+      // sure to display those errors here before the form is redirected.
+      form_display_errors($form);
+
       // Redirect the form based on values in $form_state.
       drupal_redirect_form($form_state);
     }
+    else {
+      form_display_errors($form);
+    }
 
     // Don't rebuild or cache form submissions invoked via drupal_form_submit().
     if (!empty($form_state['programmed'])) {
@@ -948,6 +955,33 @@ function drupal_process_form($form_id, &$form, &$form_state) {
 }
 
 /**
+ * Displays the given form's errors.
+ *
+ * @param $form
+ *   An associative array containing the structure of the form.
+ */
+function form_display_errors($form) {
+  if ($errors = form_get_errors()) {
+    $error_links = array();
+    foreach ($errors as $key => $error) {
+      $element = form_get_element($key, $form);
+      if ($element) {
+        $title = isset($element['#title']) ? $element['#title'] : $key;
+        $error_links[] = l($element['#title'], '', array('fragment' => 'edit-' . str_replace('_', '-', $key), 'external' => TRUE));
+      }
+      else {
+        drupal_set_message($error, 'error');
+        unset($errors[$key]);
+      }
+    }
+
+    if (!empty($error_links)) {
+      drupal_set_message(format_plural(count($error_links), '1 error has been found', '@count errors have been found') . ': ' . implode(', ', $error_links), 'error');
+    }
+  }
+}
+
+/**
  * Prepares a structured form array.
  *
  * Adds required elements, executes any hook_form_alter functions, and
@@ -1163,6 +1197,7 @@ function drupal_validate_form($form_id, &$form, &$form_state) {
         $values[$name] = $button_value;
       }
     }
+
     $form_state['values'] = $values;
   }
 }
@@ -1587,9 +1622,6 @@ function form_set_error($name = NULL, $message = '', $limit_validation_errors =
     }
     if ($record) {
       $form[$name] = $message;
-      if ($message) {
-        drupal_set_message($message, 'error');
-      }
     }
   }
 
@@ -1622,11 +1654,13 @@ function form_get_errors() {
 function form_get_error($element) {
   $form = form_set_error();
   $parents = array();
-  foreach ($element['#parents'] as $parent) {
-    $parents[] = $parent;
-    $key = implode('][', $parents);
-    if (isset($form[$key])) {
-      return $form[$key];
+  if (isset($element['#parents'])) {
+    foreach ($element['#parents'] as $parent) {
+      $parents[] = $parent;
+      $key = implode('][', $parents);
+      if (isset($form[$key])) {
+        return $form[$key];
+      }
     }
   }
 }
@@ -1921,6 +1955,37 @@ function form_builder($form_id, &$element, &$form_state) {
 }
 
 /**
+ * Given a form and an element key, this function returns the element no matter
+ * how deep within the form array the key exists. If the key is not found an
+ * empty array is returned.
+ *
+ * @param string $element_key - The key to search for.
+ *
+ * @param array $form - A structured form array to search.
+ *
+ * @return Array
+ */
+function form_get_element($element_key, $form) {
+  $element = array();
+  foreach (element_children($form) as $key) {
+    if ($key === $element_key) {
+      $element = $form[$key];
+      break;
+    }
+    else {
+      if (is_array($form[$key])) {
+        $element = form_get_element($element_key, $form[$key]);
+        if (!empty($element)) {
+          break;
+        }
+      }
+    }
+  }
+
+  return $element;
+}
+
+/**
  * Adds the #name and #value properties of an input element before rendering.
  */
 function _form_builder_handle_input_element($form_id, &$element, &$form_state) {
@@ -4266,6 +4331,11 @@ function theme_form_element($variables) {
     '#title_display' => 'before',
   );
 
+  // @todo: figure out a better place & better way to grab this error so that it
+  //         a) doesnt need this parent check
+  //         b) happens before the theme layer
+  $variables['errors'] = form_get_error($element);
+
   // Add element #id for #type 'item'.
   if (isset($element['#markup']) && !empty($element['#id'])) {
     $attributes['id'] = $element['#id'];
@@ -4282,8 +4352,17 @@ function theme_form_element($variables) {
   if (!empty($element['#attributes']['disabled'])) {
     $attributes['class'][] = 'form-disabled';
   }
+  // Add a class if an error exists.
+  if (!empty($variables['errors'])) {
+    $attributes['class'][] = 'form-error';
+  }
   $output = '<div' . drupal_attributes($attributes) . '>' . "\n";
 
+  // Display any error messages.
+  if ($variables['errors']) {
+    $output .= ' ' . theme('form_error_message', $variables);
+  }
+
   // If #title is not set, we don't display any label or required marker.
   if (!isset($element['#title'])) {
     $element['#title_display'] = 'none';
@@ -4399,6 +4478,26 @@ function theme_form_element_label($variables) {
 }
 
 /**
+ * Returns HTML for an inline error associated with a specific form element.
+ *
+ * @param $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *     Properties used: error.
+ *   - errors: The errors associated with the current element as returned by
+ *     form_get_error($element).
+ *
+ * @ingroup themeable
+ */
+function theme_form_error_message($variables) {
+  $output = '<div class="form-error-message">';
+  $output .= '<strong>' . t('Error') . ': ' . $variables['errors'] . '</strong>';
+  $output .= '</div>';
+
+  return $output;
+}
+
+/**
  * Sets a form element's class attribute.
  *
  * Adds 'required' and 'error' classes as needed.
diff --git a/core/modules/system/system.theme-rtl.css b/core/modules/system/system.theme-rtl.css
index 03c93f3..a8521b8 100644
--- a/core/modules/system/system.theme-rtl.css
+++ b/core/modules/system/system.theme-rtl.css
@@ -39,6 +39,14 @@ th {
 }
 
 /**
+ * Markup generated by Form API.
+ */
+div.form-error-message {
+  background-position: right top;
+  padding-right: 30px;
+}
+
+/**
  * Collapsible fieldsets.
  */
 .js fieldset.collapsible > legend .fieldset-legend {
diff --git a/core/modules/system/system.theme.css b/core/modules/system/system.theme.css
index d5f41e1..af79ad3 100644
--- a/core/modules/system/system.theme.css
+++ b/core/modules/system/system.theme.css
@@ -75,6 +75,18 @@ td.active {
 /**
  * Markup generated by Form API.
  */
+div.form-error {
+  background-color: #fef5f1;
+  border: 1px solid #ed541d;
+  color: #8c2e0b;
+  padding: 10px;
+}
+div.form-error-message {
+  background: no-repeat left top url(../../misc/message-24-error.png); /* LTR */
+  margin-bottom: 10px;
+  min-height: 25px;
+  padding-left: 30px; /* LTR */
+}
 .form-item,
 .form-actions {
   margin-top: 1em;
