diff --git includes/common.inc includes/common.inc
index 14e716b..0c71190 100644
--- includes/common.inc
+++ includes/common.inc
@@ -5506,6 +5506,54 @@ function element_get_visible_children(array $elements) {
 }
 
 /**
+ * Sets a value in a deeply nested array.
+ *
+ * @param $array
+ *   A reference to the array to modify.
+ * @param $parents
+ *   An ordered array of parent keys, starting with the outer most entry.
+ * @param $value
+ *   The value to set.
+ */
+function drupal_set_array_deep(&$array, $parents, $value) {
+  $parent = array_shift($parents);
+  if (empty($parents)) {
+    $array[$parent] = $value;
+  }
+  else {
+    if (!isset($array[$parent])) {
+      $array[$parent] = array();
+    }
+    drupal_set_array_deep($array[$parent], $parents, $value);
+  }
+}
+
+/**
+ * Gets a value sitting in a deeply nested array.
+ *
+ * @param $array
+ *   The array from which to get a value.
+ * @param $parents
+ *   An ordered array of parent keys of the value, starting with the outer most
+ *   entry.
+ * @return
+ *   An array with the first entry being the value. As second entry a boolean
+ *   flag is returned, which determines whether all given parent keys are set.
+ */
+function drupal_get_array_deep($array, $parents) {
+  $parent = array_shift($parents);
+  if (array_key_exists($parent, $array)) {
+    if (empty($parents)) {
+      return array($array[$parent], TRUE);
+    }
+    if (is_array($array[$parent])) {
+      return drupal_get_array_deep($array[$parent], $parents);
+    }
+  }
+  return array(NULL, FALSE);
+}
+
+/**
  * Provide theme registration for themes across .inc files.
  */
 function drupal_common_theme() {
diff --git includes/form.inc includes/form.inc
index 9e100e1..a041a3b 100644
--- includes/form.inc
+++ includes/form.inc
@@ -709,6 +709,7 @@ function drupal_retrieve_form($form_id, &$form_state) {
  *   data, is also accumulated here.
  */
 function drupal_process_form($form_id, &$form, &$form_state) {
+  drupal_set_processed_form($form, $form_state);
   $form_state['values'] = array();
 
   // With $_GET, these forms are always submitted if requested.
@@ -785,6 +786,34 @@ function drupal_process_form($form_id, &$form, &$form_state) {
       drupal_redirect_form($form_state);
     }
   }
+  // Reset the currently processed form.
+  drupal_static_reset('drupal_processed_form');
+}
+
+/**
+ * Sets the currently processed form.
+ *
+ * @see drupal_get_processed_form()
+ * @see form_set_error()
+ * @see drupal_process_form()
+ */
+function drupal_set_processed_form(&$form, &$form_state) {
+  $processed_form = &drupal_static('drupal_processed_form');
+  $processed_form = array(&$form, &$form_state);
+}
+
+/**
+ * Returns the currently processed form.
+ *
+ * @return
+ *   An array containing two elements, the currently processed form and its
+ *   form state. If there is currently no form processed, both entries are NULL.
+ *
+ * @see drupal_set_processed_form()
+ */
+function &drupal_get_processed_form() {
+  $processed_form = &drupal_static('drupal_processed_form', array(0 => NULL, 1 => NULL));
+  return $processed_form;
 }
 
 /**
@@ -915,6 +944,32 @@ function drupal_validate_form($form_id, &$form, &$form_state) {
     }
   }
 
+  // While this form is being validated, it may be desired that some calls
+  // to form_set_error() be suppressed and not result in a form error, so
+  // that a button that implements low-risk functionality (such as "Previous"
+  // or "Add more") that doesn't require all user input to be valid can still
+  // have its submit handlers triggered. The triggering element's
+  // #limit_validation_errors property contains the information for which
+  // errors are needed, and all other errors are to be suppressed. The
+  // #limit_validation_errors property is ignored if submit handlers will run,
+  // but the element doesn't have a #submit property, because it's too large a
+  // security risk to have any invalid user input when executing form-level
+  // submit handlers.
+  if (isset($form_state['triggering_element']['#limit_validation_errors']) && $form_state['submitted'] && !isset($form_state['triggering_element']['#submit'])) {
+    $form_state['triggering_element']['#limit_validation_errors'] = FALSE;
+  }
+  // If submit handlers won't run (due to the submission having been triggered
+  // by an element whose #executes_submit_callback property isn't TRUE), then
+  // it's safe to suppress all validation errors, and we do so by default,
+  // which is particularly useful during an AJAX submission triggered by a
+  // non-button. An element can override this default by setting the
+  // #limit_validation_errors property. For button element types,
+  // #limit_validation_errors defaults to FALSE (via system_element_info()),
+  // so that full validation is their default behavior.
+  elseif (isset($form_state['triggering_element']) && !isset($form_state['triggering_element']['#limit_validation_errors']) && !$form_state['submitted']) {
+    $form_state['triggering_element']['#limit_validation_errors'] = array();
+  }
+
   _form_validate($form, $form_state, $form_id);
   $validated_forms[$form_id] = TRUE;
 }
@@ -1050,39 +1105,6 @@ function _form_validate(&$elements, &$form_state, $form_id = NULL) {
       }
     }
 
-    // While this element is being validated, it may be desired that some calls
-    // to form_set_error() be suppressed and not result in a form error, so
-    // that a button that implements low-risk functionality (such as "Previous"
-    // or "Add more") that doesn't require all user input to be valid can still
-    // have its submit handlers triggered. The triggering element's
-    // #limit_validation_errors property contains the information for which
-    // errors are needed, and all other errors are to be suppressed. The
-    // #limit_validation_errors property is ignored if submit handlers will run,
-    // but the element doesn't have a #submit property, because it's too large a
-    // security risk to have any invalid user input when executing form-level
-    // submit handlers.
-    if (isset($form_state['triggering_element']['#limit_validation_errors']) && ($form_state['triggering_element']['#limit_validation_errors'] !== FALSE) && !($form_state['submitted'] && !isset($form_state['triggering_element']['#submit']))) {
-      form_set_error(NULL, '', $form_state['triggering_element']['#limit_validation_errors']);
-    }
-    // If submit handlers won't run (due to the submission having been triggered
-    // by an element whose #executes_submit_callback property isn't TRUE), then
-    // it's safe to suppress all validation errors, and we do so by default,
-    // which is particularly useful during an AJAX submission triggered by a
-    // non-button. An element can override this default by setting the
-    // #limit_validation_errors property. For button element types,
-    // #limit_validation_errors defaults to FALSE (via system_element_info()),
-    // so that full validation is their default behavior.
-    elseif (isset($form_state['triggering_element']) && !isset($form_state['triggering_element']['#limit_validation_errors']) && !$form_state['submitted']) {
-      form_set_error(NULL, '', array());
-    }
-    // As an extra security measure, explicitly turn off error suppression if
-    // one of the above conditions wasn't met. Since this is also done at the
-    // end of this function, doing it here is only to handle the rare edge case
-    // where a validate handler invokes form processing of another form.
-    else {
-      drupal_static_reset('form_set_error:limit_validation_errors');
-    }
-
     // Make sure a value is passed when the field is required.
     // A simple call to empty() will not cut it here as some fields, like
     // checkboxes, can return a valid value of '0'. Instead, check the
@@ -1106,11 +1128,6 @@ function _form_validate(&$elements, &$form_state, $form_id = NULL) {
     }
     $elements['#validated'] = TRUE;
   }
-
-  // Done validating this element, so turn off error suppression.
-  // _form_validate() turns it on again when starting on the next element, if
-  // it's still appropriate to do so.
-  drupal_static_reset('form_set_error:limit_validation_errors');
 }
 
 /**
@@ -1248,22 +1265,16 @@ function form_execute_handlers($type, &$form, &$form_state) {
  *   element where the #parents array starts with 'foo'.
  * @param $message
  *   The error message to present to the user.
- * @param $limit_validation_errors
- *   Internal use only. The #limit_validation_errors property of the clicked
- *   button, if it exists.
- *
- * @return
- *   Return value is for internal use only. To get a list of errors, use
- *   form_get_errors() or form_get_error().
  */
-function form_set_error($name = NULL, $message = '', $limit_validation_errors = NULL) {
-  $form = &drupal_static(__FUNCTION__, array());
-  $sections = &drupal_static(__FUNCTION__ . ':limit_validation_errors');
-  if (isset($limit_validation_errors)) {
-    $sections = $limit_validation_errors;
-  }
+function form_set_error($name = NULL, $message = '') {
+  $form_errors = &drupal_static(__FUNCTION__, array());
+  $static = &drupal_get_processed_form();
+  $form_state = &$static[1];
 
-  if (isset($name) && !isset($form[$name])) {
+  if (isset($form_state['triggering_element']['#limit_validation_errors']) && $form_state['triggering_element']['#limit_validation_errors'] !== FALSE) {
+    $sections = $form_state['triggering_element']['#limit_validation_errors'];
+  }
+  if (isset($name) && !isset($form_errors[$name])) {
     $record = TRUE;
     if (isset($sections)) {
       // #limit_validation_errors is an array of "sections" within which user
@@ -1287,28 +1298,24 @@ function form_set_error($name = NULL, $message = '', $limit_validation_errors =
       }
     }
     if ($record) {
-      $form[$name] = $message;
+      $form_errors[$name] = $message;
       if ($message) {
         drupal_set_message($message, 'error');
       }
     }
+    else {
+      // We do not record the validation failure, but in order to ensure safe
+      // form values we remove the value of the element failing validation.
+      drupal_set_array_deep($form_state['values'], explode('][', $name), NULL);
+    }
   }
-
-  return $form;
-}
-
-/**
- * Clear all errors against all form elements made by form_set_error().
- */
-function form_clear_error() {
-  drupal_static_reset('form_set_error');
 }
 
 /**
  * Return an associative array of all errors.
  */
 function form_get_errors() {
-  $form = form_set_error();
+  $form = &drupal_static('form_set_error', array());
   if (!empty($form)) {
     return $form;
   }
@@ -1318,7 +1325,7 @@ function form_get_errors() {
  * Return the error message filed against the form with the specified name.
  */
 function form_get_error($element) {
-  $form = form_set_error();
+  $form = &drupal_static('form_set_error', array());
   $key = $element['#parents'][0];
   if (isset($form[$key])) {
     return $form[$key];
@@ -1609,11 +1616,9 @@ function _form_builder_handle_input_element($form_id, &$element, &$form_state) {
       // submit explicit NULL values when calling drupal_form_submit(), so we do
       // not modify $form_state['input'] for them.
       if (!$input_exists && !$form_state['rebuild'] && !$form_state['programmed']) {
-        // We leverage the internal logic of form_set_value() to change the
-        // input values by passing $form_state['input'] instead of the usual
-        // $form_state['values']. In effect, this adds the necessary parent keys
-        // to $form_state['input'] and sets the element's input value to NULL.
-        _form_set_value($form_state['input'], $element, $element['#parents'], NULL);
+        // Add the necessary parent keys to $form_state['input'] and sets the
+        // element's input value to NULL.
+        drupal_set_array_deep($form_state['input'], $element['#parents'], NULL);
         $input_exists = TRUE;
       }
       // If we have input for the current element, assign it to the #value
@@ -2020,26 +2025,7 @@ function form_type_token_value($element, $input = FALSE) {
  *   Form state array where the value change should be recorded.
  */
 function form_set_value($element, $value, &$form_state) {
-  _form_set_value($form_state['values'], $element, $element['#parents'], $value);
-}
-
-/**
- * Helper function for form_set_value() and _form_builder_handle_input_element().
- *
- * We iterate over $parents and create nested arrays for them in $form_values if
- * needed. Then we insert the value into the last parent key.
- */
-function _form_set_value(&$form_values, $element, $parents, $value) {
-  $parent = array_shift($parents);
-  if (empty($parents)) {
-    $form_values[$parent] = $value;
-  }
-  else {
-    if (!isset($form_values[$parent])) {
-      $form_values[$parent] = array();
-    }
-    _form_set_value($form_values[$parent], $element, $parents, $value);
-  }
+  drupal_set_array_deep($form_state['values'], $element['#parents'], $value);
 }
 
 /**
