diff --git a/ife.module b/ife.module
index 5e738b6..54b1019 100644
--- a/ife.module
+++ b/ife.module
@@ -225,8 +225,22 @@ function ife_form_validator($form, &$form_state) {
   static $global_error_processed = FALSE;
 
   $form_errors = form_get_errors();
+
   if (!empty($form_errors)) {
-    ife_element_errors_set($form, $form['#ife_display']);
+
+    // Attach the errors to the form elements.
+    ife_attach_errors($form, $form['#ife_display']);
+
+    // Clean up the session variable. Remove the error array if it's
+    // empty, otherwise renumber the keys.
+    if (isset($_SESSION['messages']['error'])) {
+      if (count($_SESSION['messages']['error']) == 0) {
+        unset($_SESSION['messages']['error']);
+      }
+      else {
+        $_SESSION['messages']['error'] = array_values($_SESSION['messages']['error']);
+      }
+    }
 
     if ($form['#ife_display'] == 1 && !$global_error_processed) {
       $message = filter_xss_admin(variable_get('ife_general_message', 'Please correct all highlighted errors and try again.'));
@@ -258,39 +272,84 @@ function ife_errors($op = 'get', $id = NULL, $message = NULL) {
   }
 }
 
-function ife_element_errors_set($element, $display) {
-  if (!isset($_SESSION['messages'])) {
-    return;
-  }
-
-  // Recurse through all children.
-  foreach (element_children($element) as $key) {
-    if (isset($element[$key]) && $element[$key]) {
-      ife_element_errors_set($element[$key], $display);
-    }
-  }
-
-  // Check for errors and settings
-  $errors = form_get_errors();
-  $element_id = implode('][', $element['#parents']);
-  if (!empty($errors[$element_id])) {
-    $error_message = $errors[$element_id];
-
-    // Get error id
-    $error_id = array_search($error_message, $_SESSION['messages']['error']);
-
-    if ($error_id !== FALSE) {
-      if (isset($display) && $display != 0) {
-        unset($_SESSION['messages']['error'][$error_id]);
-        $_SESSION['messages']['error'] = array_values($_SESSION['messages']['error']);
+/**
+ * Associate error messages with the fields they've been raised against.
+ *
+ * We treat the form array as a rooted tree and perform a depth first search
+ * across the vertices. At each vertex we check if an error message has been
+ * assigned to that element. If any of the elements ancestors has already been
+ * assigned that error message we ignore it. If this is the first unrelated
+ * element to receive the error message it is added as a property to the
+ * element.
+ *
+ * This should ensure that error messages which apply to a group of closely
+ * positioned fields only appear once. Where as fields which have the same error
+ * message but appear independently each receive the message.
+ *
+ * Ultimately, there will be edge cases where this heuristic fails and these
+ * should be treated on a case by case basis.
+ *
+ * @param array $elements
+ *   The elements to check for error messages.
+ * @param int $display
+ *   What to do with the error messages raised by Drupal.
+ *     - 0: Leave the messages in place.
+ *     - Anything else: Remove all messages.
+ * @param array $ignored_keys = array()
+ *   An array of error keys. The error messages which have these keys won't be
+ *   attached to any of the provided elements. This parameter is used internally
+ *   to maintain a list of the errors which have already been associated with an
+ *   element's ancestors.
+ */
+function ife_attach_errors($elements, $display, $ignored_keys = array()) {
+  // Loop through each of the child elements of the form.
+  foreach (element_children($elements) as $key) {
+    // If the element has an error associated with it, we added it and stop
+    // descending down this branch of the tree.
+    $error = ife_form_get_error($elements[$key]);
+
+    // Create an array of all the keys children of this element should ignore.
+    // Start with just the ones that we're currently ignoring.
+    $element_ignored_keys = $ignored_keys;
+
+    if ($error && !in_array($error->key, $ignored_keys)) {
+      // Set the error message.
+      ife_errors('set', $elements[$key]['#id'], $error->message);
+
+      // If we're raising an error here don't raise it again on any of the
+      // child elements.
+      $element_ignored_keys[] = $error->key;
+
+      // Check to see if the error has been set in the session also and remove
+      // it if necessary.
+      $session_error_id = array_search($error->message, $_SESSION['messages']['error']);
+      if ($session_error_id !== FALSE && isset($display) && $display != 0) {
+        unset($_SESSION['messages']['error'][$session_error_id]);
       }
+    }
 
-      if (count($_SESSION['messages']['error']) <= 0) {
-        unset($_SESSION['messages']['error']);
-      }
+    // Continue searching the child elements of this element.
+    ife_attach_errors($elements[$key], $display, $element_ignored_keys);
+  }
+}
 
-      // Set error message in session, so it can be used in our theming.
-      ife_errors('set', $element['#id'], $error_message);
+/**
+ * Returns the error message filed against the given form element.
+ *
+ * Form errors higher up in the form structure override deeper errors as well as
+ * errors on the element itself.
+ */
+function ife_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 (object) array(
+        'key' => $key,
+        'message' => $form[$key]
+      );
     }
   }
 }
@@ -309,18 +368,7 @@ function ife_commerce_checkout_pane_info_alter(&$panes) {
 }
 
 function ife_element_info_alter(&$types) {
-  // List of form elements which are childs or don't have a need form inline errors
-  $excluded_fields = array('radio', 'hidden', 'token', 'value');
-
-  foreach($types as $key => $value) {
-    // Element with input, aka FAPI field.
-    if (isset($value['#input']) && $value['#input'] && !in_array($value['#type'], $excluded_fields)) {
-      if (!isset($types[$key]['#theme_wrappers'])) {
-        $types[$key]['#theme_wrappers'] = array('ife_form_element');
-      }
-      else {
-        $types[$key]['#theme_wrappers'][] = 'ife_form_element';
-      }
-    }
+  foreach($types as &$type) {
+    $type['#theme_wrappers'][] = 'ife_form_element';
   }
 }
