diff --git a/includes/authorize.inc b/includes/authorize.inc
index 3617d7d..81a8055 100644
--- a/includes/authorize.inc
+++ b/includes/authorize.inc
@@ -209,7 +209,7 @@ function authorize_filetransfer_form_validate($form, &$form_state) {
     catch (Exception $e) {
       // The format of this error message is similar to that used on the
       // database connection form in the installer.
-      form_set_error('connection_settings', t('Failed to connect to the server. The server reports the following message: !message For more help installing or updating code on your server, see the <a href="@handbook_url">handbook</a>.', array(
+      form_error($form['connection_settings'], $form_state, t('Failed to connect to the server. The server reports the following message: !message For more help installing or updating code on your server, see the <a href="@handbook_url">handbook</a>.', array(
         '!message' => '<p class="error">' . $e->getMessage()  . '</p>',
         '@handbook_url' => 'http://drupal.org/documentation/install/modules-themes',
       )));
diff --git a/includes/form.inc b/includes/form.inc
index 8f2ee26..4be5e78 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -381,6 +381,8 @@ function form_state_defaults() {
     'method' => 'post',
     'groups' => array(),
     'buttons' => array(),
+    'errors' => array(),
+    'limit_validation_errors' => NULL,
   );
 }
 
@@ -603,7 +605,7 @@ function form_load_include(&$form_state, $type, $module, $name = NULL) {
  * processes a form, but does not allow you to supply values.
  *
  * There is no return value, but you can check to see if there are errors
- * by calling form_get_errors().
+ * by calling form_get_errors($form_state).
  *
  * @param $form_id
  *   The unique string identifying the desired form. If a function
@@ -673,7 +675,7 @@ function drupal_form_submit($form_id, &$form_state) {
 
   // Reset form validation.
   $form_state['must_validate'] = TRUE;
-  form_clear_error();
+  $form_state['errors'] = array();
 
   drupal_prepare_form($form_id, $form, $form_state);
   drupal_process_form($form_id, $form, $form_state);
@@ -824,7 +826,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'] && empty($form_state['errors']) && !$form_state['rebuild']) {
       // Execute form submit handlers.
       form_execute_handlers('submit', $form, $form_state);
 
@@ -891,7 +893,7 @@ function drupal_process_form($form_id, &$form, &$form_state) {
     //   along with element-level #submit properties, it makes no sense to have
     //   divergent form execution based on whether the triggering element has
     //   #executes_submit_callback set to TRUE.
-    if (($form_state['rebuild'] || !$form_state['executed']) && !form_get_errors()) {
+    if (($form_state['rebuild'] || !$form_state['executed']) && empty($form_state['errors'])) {
       // Form building functions (e.g., _form_builder_handle_input_element())
       // may use $form_state['rebuild'] to determine if they are running in the
       // context of a rebuild, so ensure it is set.
@@ -1076,7 +1078,7 @@ function drupal_validate_form($form_id, &$form, &$form_state) {
   if (isset($form['#token'])) {
     if (!drupal_valid_token($form_state['values']['form_token'], $form['#token'])) {
       // Setting this error will cause the form to fail validation.
-      form_set_error('form_token', t('This form is outdated. Reload the page and try again. Contact the site administrator if the problem persists.'));
+      form_error($form['form_token'], $form_state, t('This form is outdated. Reload the page and try again. Contact the site administrator if the problem persists.'));
     }
   }
 
@@ -1229,7 +1231,7 @@ function _form_validate(&$elements, &$form_state, $form_id = NULL) {
     if (isset($elements['#needs_validation'])) {
       // Verify that the value is not longer than #maxlength.
       if (isset($elements['#maxlength']) && drupal_strlen($elements['#value']) > $elements['#maxlength']) {
-        form_error($elements, $t('!name cannot be longer than %max characters but is currently %length characters long.', array('!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title'], '%max' => $elements['#maxlength'], '%length' => drupal_strlen($elements['#value']))));
+        form_error($elements, $form_state, $t('!name cannot be longer than %max characters but is currently %length characters long.', array('!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title'], '%max' => $elements['#maxlength'], '%length' => drupal_strlen($elements['#value']))));
       }
 
       if (isset($elements['#options']) && isset($elements['#value'])) {
@@ -1243,7 +1245,7 @@ function _form_validate(&$elements, &$form_state, $form_id = NULL) {
           $value = in_array($elements['#type'], array('checkboxes', 'tableselect')) ? array_keys($elements['#value']) : $elements['#value'];
           foreach ($value as $v) {
             if (!isset($options[$v])) {
-              form_error($elements, $t('An illegal choice has been detected. Please contact the site administrator.'));
+              form_error($elements, $form_state, $t('An illegal choice has been detected. Please contact the site administrator.'));
               watchdog('form', 'Illegal choice %choice in !name element.', array('%choice' => $v, '!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), LOG_ERR);
             }
           }
@@ -1262,15 +1264,15 @@ function _form_validate(&$elements, &$form_state, $form_id = NULL) {
           form_set_value($elements, NULL, $form_state);
         }
         elseif (!isset($options[$elements['#value']])) {
-          form_error($elements, $t('An illegal choice has been detected. Please contact the site administrator.'));
+          form_error($elements, $form_state, $t('An illegal choice has been detected. Please contact the site administrator.'));
           watchdog('form', 'Illegal choice %choice in %name element.', array('%choice' => $elements['#value'], '%name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), LOG_ERR);
         }
       }
     }
 
     // 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"
+    // to form_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
@@ -1280,7 +1282,7 @@ function _form_validate(&$elements, &$form_state, $form_id = NULL) {
     // 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']);
+      $form_state['limit_validation_errors'] = $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
@@ -1291,14 +1293,14 @@ function _form_validate(&$elements, &$form_state, $form_id = NULL) {
     // #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());
+      $form_state['limit_validation_errors'] = 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');
+      $form_state['limit_validation_errors'] = NULL;
     }
 
     // Make sure a value is passed when the field is required.
@@ -1317,10 +1319,10 @@ function _form_validate(&$elements, &$form_state, $form_id = NULL) {
         // Instead of setting no #title, form constructors are encouraged to set
         // #title_display to 'invisible' to improve accessibility.
         if (isset($elements['#title'])) {
-          form_error($elements, $t('!name field is required.', array('!name' => $elements['#title'])));
+          form_error($elements, $form_state, $t('!name field is required.', array('!name' => $elements['#title'])));
         }
         else {
-          form_error($elements);
+          form_error($elements, $form_state);
         }
       }
     }
@@ -1337,12 +1339,18 @@ function _form_validate(&$elements, &$form_state, $form_id = NULL) {
       }
     }
     $elements['#validated'] = TRUE;
+    // Expose error messages to the output tree so they can be used during
+    // rendering and theming.
+    $key = implode('][', $element['#parents']);
+    if (!empty($form_state['errors'][$key])) {
+      $elements['#errors'] =  $form_state['errors'][$key];
+    }
   }
 
   // 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');
+  $form_state['limit_validation_errors'] = NULL;
 }
 
 /**
@@ -1396,13 +1404,13 @@ function form_execute_handlers($type, &$form, &$form_state) {
 /**
  * Files an error against a form element.
  *
- * When a validation error is detected, the validator calls form_set_error() to
+ * When a validation error is detected, the validator calls form_error() to
  * indicate which element needs to be changed and provide an error message. This
  * causes the Form API to not execute the form submit handlers, and instead to
  * re-display the form to the user with the corresponding elements rendered with
  * an 'error' CSS class (shown as red by default).
  *
- * The standard form_set_error() behavior can be changed if a button provides
+ * The standard form_error() behavior can be changed if a button provides
  * the #limit_validation_errors property. Multistep forms not wanting to
  * validate the whole form can set #limit_validation_errors on buttons to
  * limit validation errors to only certain elements. For example, pressing the
@@ -1450,13 +1458,14 @@ function form_execute_handlers($type, &$form, &$form_state) {
  *
  * This will require $form_state['values']['step1'] and everything within it
  * (for example, $form_state['values']['step1']['choice']) to be valid, so
- * calls to form_set_error('step1', $message) or
- * form_set_error('step1][choice', $message) will prevent the submit handlers
- * from running, and result in the error message being displayed to the user.
- * However, calls to form_set_error('step2', $message) and
- * form_set_error('step2][groupX][choiceY', $message) will be suppressed,
- * resulting in the message not being displayed to the user, and the submit
- * handlers will run despite $form_state['values']['step2'] and
+ * calls to form_error($form['step1'], $form_state, $message) or
+ * form_error($form['step1']['choice'], $form_state, $message) will prevent the
+ * submit handlers from running, and result in the error message being displayed
+ * to the user.
+ * However, calls to form_error($form['step2'], $form_state, $message) and
+ * form_error($form['step2']['groupX']['choiceY'], $form_state, $message) will
+ * be suppressed, resulting in the message not being displayed to the user, and
+ * the submit handlers will run despite $form_state['values']['step2'] and
  * $form_state['values']['step2']['groupX']['choiceY'] containing invalid
  * values. Errors for an invalid $form_state['values']['foo'] will be
  * suppressed, but errors flagging invalid values for
@@ -1473,31 +1482,18 @@ function form_execute_handlers($type, &$form, &$form_state) {
  * @see http://drupal.org/node/370537
  * @see http://drupal.org/node/763376
  *
- * @param $name
- *   The name of the form element. If the #parents property of your form
- *   element is array('foo', 'bar', 'baz') then you may set an error on 'foo'
- *   or 'foo][bar][baz'. Setting an error on 'foo' sets an error for every
- *   element where the #parents array starts with 'foo'.
+ * @param $element
+ *   The form element to file an error for.
+ * @param $form_state
+ *   The current state of the form.
  * @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;
-  }
-
-  if (isset($name) && !isset($form[$name])) {
+function form_error($element, &$form_state, $message = '') {
+  $name = implode('][', $element['#parents']);
+  if (isset($name) && !isset($form_state['errors'][$name])) {
     $record = TRUE;
-    if (isset($sections)) {
+    if (isset($form_state['limit_validation_errors'])) {
       // #limit_validation_errors is an array of "sections" within which user
       // input must be valid. If the element is within one of these sections,
       // the error must be recorded. Otherwise, it can be suppressed.
@@ -1506,7 +1502,7 @@ function form_set_error($name = NULL, $message = '', $limit_validation_errors =
       // submit action to be triggered even if none of the submitted values are
       // valid.
       $record = FALSE;
-      foreach ($sections as $section) {
+      foreach ($form_state['limit_validation_errors'] as $section) {
         // Exploding by '][' reconstructs the element's #parents. If the
         // reconstructed #parents begin with the same keys as the specified
         // section, then the element's values are within the part of
@@ -1521,31 +1517,20 @@ function form_set_error($name = NULL, $message = '', $limit_validation_errors =
       }
     }
     if ($record) {
-      $form[$name] = $message;
+      $form_state['errors'][$name] = $message;
       if ($message) {
+        // @todo move elsewhere? after full form validation?
         drupal_set_message($message, 'error');
       }
     }
   }
-
-  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();
-  if (!empty($form)) {
-    return $form;
-  }
+function form_get_errors(&$form_state) {
+  return !empty($form_state['errors']) ? $form_state['errors'] : NULL;
 }
 
 /**
@@ -1554,23 +1539,35 @@ function form_get_errors() {
  * Form errors higher up in the form structure override deeper errors as well as
  * errors on the element itself.
  */
-function form_get_error($element) {
-  $form = form_set_error();
+function form_get_error($element, &$form_state) {
   $parents = array();
   foreach ($element['#parents'] as $parent) {
     $parents[] = $parent;
     $key = implode('][', $parents);
-    if (isset($form[$key])) {
-      return $form[$key];
+    if (isset($form_state['errors'][$key])) {
+      return $form_state['errors'][$key];
     }
   }
 }
 
 /**
- * Flag an element as having an error.
+ * (Deprecated) Flag an element as having an error.
+ *
+ * @param $form_state
+ *   The current state of the form.
+ * @param $name
+ *   The name of the form element. If the #parents property of your form
+ *   element is array('foo', 'bar', 'baz') then you may set an error on 'foo'
+ *   or 'foo][bar][baz'. Setting an error on 'foo' sets an error for every
+ *   element where the #parents array starts with 'foo'.
+ * @param $message
+ *   The error message to present to the user.
+ *
+ * @todo Remove.
  */
-function form_error(&$element, $message = '') {
-  form_set_error(implode('][', $element['#parents']), $message);
+function form_set_error(&$form_state, $name, $message = '') {
+  $element = array('#parents' => explode('][', $name));
+  form_error($element, $form_state, $message);
 }
 
 /**
@@ -2741,11 +2738,11 @@ function password_confirm_validate($element, &$element_state) {
   $pass2 = trim($element['pass2']['#value']);
   if (!empty($pass1) || !empty($pass2)) {
     if (strcmp($pass1, $pass2)) {
-      form_error($element, t('The specified passwords do not match.'));
+      form_error($element, $element_state, t('The specified passwords do not match.'));
     }
   }
   elseif ($element['#required'] && !empty($element_state['input'])) {
-    form_error($element, t('Password field is required.'));
+    form_error($element, $element_state, t('Password field is required.'));
   }
 
   // Password field must be converted from a two-element array into a single
@@ -2833,9 +2830,9 @@ function form_process_date($element) {
 /**
  * Validates the date type to stop dates like February 30, 2006.
  */
-function date_validate($form) {
+function date_validate($form, &$form_state) {
   if (!checkdate($form['#value']['month'], $form['#value']['day'], $form['#value']['year'])) {
-    form_error($form, t('The specified date is invalid.'));
+    form_error($form, $form_state, t('The specified date is invalid.'));
   }
 }
 
@@ -3343,7 +3340,7 @@ function form_process_machine_name($element, &$form_state) {
 function form_validate_machine_name(&$element, &$form_state) {
   // Verify that the machine name not only consists of replacement tokens.
   if (preg_match('@^' . $element['#machine_name']['replace'] . '+$@', $element['#value'])) {
-    form_error($element, t('The machine-readable name must contain unique characters.'));
+    form_error($element, $form_state, t('The machine-readable name must contain unique characters.'));
   }
 
   // Verify that the machine name contains no disallowed characters.
@@ -3352,15 +3349,15 @@ function form_validate_machine_name(&$element, &$form_state) {
       // Since a hyphen is the most common alternative replacement character,
       // a corresponding validation error message is supported here.
       if ($element['#machine_name']['replace'] == '-') {
-        form_error($element, t('The machine-readable name must contain only lowercase letters, numbers, and hyphens.'));
+        form_error($element, $form_state, t('The machine-readable name must contain only lowercase letters, numbers, and hyphens.'));
       }
       // Otherwise, we assume the default (underscore).
       else {
-        form_error($element, t('The machine-readable name must contain only lowercase letters, numbers, and underscores.'));
+        form_error($element, $form_state, t('The machine-readable name must contain only lowercase letters, numbers, and underscores.'));
       }
     }
     else {
-      form_error($element, $element['#machine_name']['error']);
+      form_error($element, $form_state, $element['#machine_name']['error']);
     }
   }
 
@@ -3368,7 +3365,7 @@ function form_validate_machine_name(&$element, &$form_state) {
   if ($element['#default_value'] !== $element['#value']) {
     $function = $element['#machine_name']['exists'];
     if ($function($element['#value'], $element, $form_state)) {
-      form_error($element, t('The machine-readable name is already in use. It must be unique.'));
+      form_error($element, $form_state, t('The machine-readable name is already in use. It must be unique.'));
     }
   }
 }
@@ -3973,7 +3970,7 @@ function _form_set_class(&$element, $class = array()) {
   if (!empty($element['#required'])) {
     $element['#attributes']['class'][] = 'required';
   }
-  if (isset($element['#parents']) && form_get_error($element)) {
+  if (!empty($element['#errors'])) {
     $element['#attributes']['class'][] = 'error';
   }
 }
diff --git a/includes/install.core.inc b/includes/install.core.inc
index a74dfdf..2550da9 100644
--- a/includes/install.core.inc
+++ b/includes/install.core.inc
@@ -412,7 +412,7 @@ function install_run_task($task, &$install_state) {
         'build_info' => array('args' => array(&$install_state)),
       );
       drupal_form_submit($function, $form_state);
-      $errors = form_get_errors();
+      $errors = form_get_errors($form_state);
       if (!empty($errors)) {
         throw new Exception(implode("\n", $errors));
       }
@@ -912,7 +912,7 @@ function install_settings_form_validate($form, &$form_state) {
   $form_state['storage']['database'] = $database;
   $errors = install_database_errors($database, $form_state['values']['settings_file']);
   foreach ($errors as $name => $message) {
-    form_set_error($name, $message);
+    form_error($form[$name], $form_state, $message);
   }
 }
 
@@ -1782,13 +1782,13 @@ function _install_configure_form($form, &$form_state, &$install_state) {
  */
 function install_configure_form_validate($form, &$form_state) {
   if ($error = user_validate_name($form_state['values']['account']['name'])) {
-    form_error($form['admin_account']['account']['name'], $error);
+    form_error($form['admin_account']['account']['name'], $form_state, $error);
   }
   if ($error = user_validate_mail($form_state['values']['account']['mail'])) {
-    form_error($form['admin_account']['account']['mail'], $error);
+    form_error($form['admin_account']['account']['mail'], $form_state, $error);
   }
   if ($error = user_validate_mail($form_state['values']['site_mail'])) {
-    form_error($form['site_information']['site_mail'], $error);
+    form_error($form['site_information']['site_mail'], $form_state, $error);
   }
 }
 
diff --git a/includes/unicode.inc b/includes/unicode.inc
index df91868..f51ed52 100644
--- a/includes/unicode.inc
+++ b/includes/unicode.inc
@@ -93,7 +93,7 @@ function unicode_check() {
  * be disabled for similar reasons.
  *
  * @param $errors
- *   Whether to report any fatal errors with form_set_error().
+ *   Whether to report any fatal errors with form_error().
  */
 function _unicode_check() {
   // Ensure translations don't break at install time
diff --git a/modules/aggregator/aggregator.admin.inc b/modules/aggregator/aggregator.admin.inc
index d9039e0..f2567a3 100644
--- a/modules/aggregator/aggregator.admin.inc
+++ b/modules/aggregator/aggregator.admin.inc
@@ -135,7 +135,7 @@ function aggregator_form_feed_validate($form, &$form_state) {
   if ($form_state['values']['op'] == t('Save')) {
     // Ensure URL is valid.
     if (!valid_url($form_state['values']['url'], TRUE)) {
-      form_set_error('url', t('The URL %url is invalid. Enter a fully-qualified URL, such as http://www.example.com/feed.xml.', array('%url' => $form_state['values']['url'])));
+      form_error($form['url'], $form_state, t('The URL %url is invalid. Enter a fully-qualified URL, such as http://www.example.com/feed.xml.', array('%url' => $form_state['values']['url'])));
     }
     // Check for duplicate titles.
     if (isset($form_state['values']['fid'])) {
@@ -146,10 +146,10 @@ function aggregator_form_feed_validate($form, &$form_state) {
     }
     foreach ($result as $feed) {
       if (strcasecmp($feed->title, $form_state['values']['title']) == 0) {
-        form_set_error('title', t('A feed named %feed already exists. Enter a unique title.', array('%feed' => $form_state['values']['title'])));
+        form_error($form['title'], $form_state, t('A feed named %feed already exists. Enter a unique title.', array('%feed' => $form_state['values']['title'])));
       }
       if (strcasecmp($feed->url, $form_state['values']['url']) == 0) {
-        form_set_error('url', t('A feed with this URL %url already exists. Enter a unique URL.', array('%url' => $form_state['values']['url'])));
+        form_error($form['url'], $form_state, t('A feed with this URL %url already exists. Enter a unique URL.', array('%url' => $form_state['values']['url'])));
       }
     }
   }
@@ -285,12 +285,12 @@ function aggregator_form_opml($form, &$form_state) {
 function aggregator_form_opml_validate($form, &$form_state) {
   // If both fields are empty or filled, cancel.
   if (empty($form_state['values']['remote']) == empty($_FILES['files']['name']['upload'])) {
-    form_set_error('remote', t('You must <em>either</em> upload a file or enter a URL.'));
+    form_error($form['remote'], $form_state, t('You must <em>either</em> upload a file or enter a URL.'));
   }
 
   // Validate the URL, if one was entered.
   if (!empty($form_state['values']['remote']) && !valid_url($form_state['values']['remote'], TRUE)) {
-    form_set_error('remote', t('This URL is not valid.'));
+    form_error($form['remote'], $form_state, t('This URL is not valid.'));
   }
 }
 
@@ -548,7 +548,7 @@ function aggregator_form_category_validate($form, &$form_state) {
       $category = db_query("SELECT cid FROM {aggregator_category} WHERE title = :title", array(':title' => $form_state['values']['title']))->fetchObject();
     }
     if ($category) {
-      form_set_error('title', t('A category named %category already exists. Enter a unique title.', array('%category' => $form_state['values']['title'])));
+      form_error($form['title'], $form_state, t('A category named %category already exists. Enter a unique title.', array('%category' => $form_state['values']['title'])));
     }
   }
 }
diff --git a/modules/block/block.admin.inc b/modules/block/block.admin.inc
index 7cf299c..300d6ef 100644
--- a/modules/block/block.admin.inc
+++ b/modules/block/block.admin.inc
@@ -447,7 +447,7 @@ function block_admin_configure_validate($form, &$form_state) {
       ':info' => $form_state['values']['info'],
     ))->fetchField();
     if (empty($form_state['values']['info']) || $custom_block_exists) {
-      form_set_error('info', t('Ensure that each block description is unique.'));
+      form_error($form['info'], $form_state, t('Ensure that each block description is unique.'));
     }
   }
 }
@@ -459,7 +459,7 @@ function block_admin_configure_validate($form, &$form_state) {
  * @see block_admin_configure_validate()
  */
 function block_admin_configure_submit($form, &$form_state) {
-  if (!form_get_errors()) {
+  if (form_get_errors($form_state)) {
     $txn = db_transaction();
 
     db_update('block')
@@ -527,7 +527,7 @@ function block_add_block_form_validate($form, &$form_state) {
   $custom_block_exists = (bool) db_query_range('SELECT 1 FROM {block_custom} WHERE info = :info', 0, 1, array(':info' => $form_state['values']['info']))->fetchField();
 
   if (empty($form_state['values']['info']) || $custom_block_exists) {
-    form_set_error('info', t('Ensure that each block description is unique.'));
+    form_error($form['info'], $form_state, t('Ensure that each block description is unique.'));
   }
 }
 
diff --git a/modules/book/book.admin.inc b/modules/book/book.admin.inc
index 58fd4f9..7250ec8 100644
--- a/modules/book/book.admin.inc
+++ b/modules/book/book.admin.inc
@@ -59,7 +59,7 @@ function book_admin_settings() {
 function book_admin_settings_validate($form, &$form_state) {
   $child_type = $form_state['values']['book_child_type'];
   if (empty($form_state['values']['book_allowed_types'][$child_type])) {
-    form_set_error('book_child_type', t('The content type for the %add-child link must be one of those selected as an allowed book outline type.', array('%add-child' => t('Add child page'))));
+    form_error($form['book_child_type'], $form_state, t('The content type for the %add-child link must be one of those selected as an allowed book outline type.', array('%add-child' => t('Add child page'))));
   }
 }
 
@@ -89,7 +89,7 @@ function book_admin_edit($form, $form_state, $node) {
  */
 function book_admin_edit_validate($form, &$form_state) {
   if ($form_state['values']['tree_hash'] != $form_state['values']['tree_current_hash']) {
-    form_set_error('', t('This book has been modified by another user, the changes could not be saved.'));
+    form_error($form, $form_state, t('This book has been modified by another user, the changes could not be saved.'));
   }
 }
 
diff --git a/modules/comment/comment.admin.inc b/modules/comment/comment.admin.inc
index 4f3d350..1181112 100644
--- a/modules/comment/comment.admin.inc
+++ b/modules/comment/comment.admin.inc
@@ -146,7 +146,7 @@ function comment_admin_overview_validate($form, &$form_state) {
   $form_state['values']['comments'] = array_diff($form_state['values']['comments'], array(0));
   // We can't execute any 'Update options' if no comments were selected.
   if (count($form_state['values']['comments']) == 0) {
-    form_set_error('', t('Select one or more comments to perform the update on.'));
+    form_error($form, $form_state, t('Select one or more comments to perform the update on.'));
   }
 }
 
diff --git a/modules/comment/comment.module b/modules/comment/comment.module
index 60a9ca4..262d009 100644
--- a/modules/comment/comment.module
+++ b/modules/comment/comment.module
@@ -2094,10 +2094,10 @@ function comment_form_validate($form, &$form_state) {
     $form_state['values']['uid'] = $account ? $account->uid : 0;
 
     if ($form_state['values']['date'] && strtotime($form_state['values']['date']) === FALSE) {
-      form_set_error('date', t('You have to specify a valid date.'));
+      form_error($form['date'], $form_state, t('You have to specify a valid date.'));
     }
     if ($form_state['values']['name'] && !$form_state['values']['is_anonymous'] && !$account) {
-      form_set_error('name', t('You have to specify a valid author.'));
+      form_error($form['name'], $form_state, t('You have to specify a valid author.'));
     }
   }
   elseif ($form_state['values']['is_anonymous']) {
@@ -2113,15 +2113,15 @@ function comment_form_validate($form, &$form_state) {
         ->execute()
         ->fetchField();
       if ($taken) {
-        form_set_error('name', t('The name you used belongs to a registered user.'));
+        form_error($form['name'], $form_state, t('The name you used belongs to a registered user.'));
       }
     }
   }
   if ($form_state['values']['mail'] && !valid_email_address($form_state['values']['mail'])) {
-    form_set_error('mail', t('The e-mail address you specified is not valid.'));
+    form_error($form['mail'], $form_state, t('The e-mail address you specified is not valid.'));
   }
   if ($form_state['values']['homepage'] && !valid_url($form_state['values']['homepage'], TRUE)) {
-    form_set_error('homepage', t('The URL of your homepage is not valid. Remember that it must be fully qualified, i.e. of the form <code>http://example.com/directory</code>.'));
+    form_error($form['homepage'], $form_state, t('The URL of your homepage is not valid. Remember that it must be fully qualified, i.e. of the form <code>http://example.com/directory</code>.'));
   }
 }
 
diff --git a/modules/contact/contact.admin.inc b/modules/contact/contact.admin.inc
index bc555c4..d3c15bb 100644
--- a/modules/contact/contact.admin.inc
+++ b/modules/contact/contact.admin.inc
@@ -131,13 +131,13 @@ function contact_category_edit_form_validate($form, &$form_state) {
     $query->condition('c.cid', $form_state['values']['cid'], '<>');
   }
   if ($query->countQuery()->execute()->fetchField()) {
-    form_set_error('category', t('A contact form with category %category already exists.', array('%category' => $category)));
+    form_error($form['category'], $form_state, t('A contact form with category %category already exists.', array('%category' => $category)));
   }
 
   foreach ($recipients as &$recipient) {
     $recipient = trim($recipient);
     if (!valid_email_address($recipient)) {
-      form_set_error('recipients', t('%recipient is an invalid e-mail address.', array('%recipient' => $recipient)));
+      form_error($form['recipients'], $form_state, t('%recipient is an invalid e-mail address.', array('%recipient' => $recipient)));
     }
   }
   $form_state['values']['recipients'] = implode(',', $recipients);
diff --git a/modules/contact/contact.pages.inc b/modules/contact/contact.pages.inc
index 30b2825..0740f0f 100644
--- a/modules/contact/contact.pages.inc
+++ b/modules/contact/contact.pages.inc
@@ -115,10 +115,10 @@ function contact_site_form($form, &$form_state) {
  */
 function contact_site_form_validate($form, &$form_state) {
   if (!$form_state['values']['cid']) {
-    form_set_error('cid', t('You must select a valid category.'));
+    form_error($form['cid'], $form_state, t('You must select a valid category.'));
   }
   if (!valid_email_address($form_state['values']['mail'])) {
-    form_set_error('mail', t('You must enter a valid e-mail address.'));
+    form_error($form['mail'], $form_state, t('You must enter a valid e-mail address.'));
   }
 }
 
@@ -248,7 +248,7 @@ function contact_personal_form($form, &$form_state, $recipient) {
  */
 function contact_personal_form_validate($form, &$form_state) {
   if (!valid_email_address($form_state['values']['mail'])) {
-    form_set_error('mail', t('You must enter a valid e-mail address.'));
+    form_error($form['mail'], $form_state, t('You must enter a valid e-mail address.'));
   }
 }
 
diff --git a/modules/dblog/dblog.admin.inc b/modules/dblog/dblog.admin.inc
index ef8f935..a70a996 100644
--- a/modules/dblog/dblog.admin.inc
+++ b/modules/dblog/dblog.admin.inc
@@ -323,7 +323,7 @@ function dblog_filter_form($form) {
  */
 function dblog_filter_form_validate($form, &$form_state) {
   if ($form_state['values']['op'] == t('Filter') && empty($form_state['values']['type']) && empty($form_state['values']['severity'])) {
-    form_set_error('type', t('You must select something to filter by.'));
+    form_error($form['type'], $form_state, t('You must select something to filter by.'));
   }
 }
 
diff --git a/modules/field/field.api.php b/modules/field/field.api.php
index fae598d..363fd1f 100644
--- a/modules/field/field.api.php
+++ b/modules/field/field.api.php
@@ -855,7 +855,7 @@ function hook_field_widget_form(&$form, &$form_state, $field, $instance, $langco
  *   An associative array containing the current state of the form.
  */
 function hook_field_widget_error($element, $error, $form, &$form_state) {
-  form_error($element['value'], $error['message']);
+  form_error($element['value'], $form_state, $error['message']);
 }
 
 /**
diff --git a/modules/field/field.form.inc b/modules/field/field.form.inc
index 845f041..1fc8c04 100644
--- a/modules/field/field.form.inc
+++ b/modules/field/field.form.inc
@@ -353,7 +353,7 @@ function field_default_form_errors($entity_type, $entity, $field, $instance, $la
         else {
           // Make sure that errors are reported (even incorrectly flagged) if
           // the widget module fails to implement hook_field_widget_error().
-          form_error($error_element, $error['error']);
+          form_error($error_element, $form_state, $error['error']);
         }
       }
     }
diff --git a/modules/field/field.module b/modules/field/field.module
index b808e59..b566bd4 100644
--- a/modules/field/field.module
+++ b/modules/field/field.module
@@ -1181,7 +1181,7 @@ function theme_field($variables) {
 function _element_validate_integer($element, &$form_state) {
   $value = $element['#value'];
   if ($value !== '' && (!is_numeric($value) || intval($value) != $value)) {
-    form_error($element, t('%name must be an integer.', array('%name' => $element['#title'])));
+    form_error($element, $form_state, t('%name must be an integer.', array('%name' => $element['#title'])));
   }
 }
 
@@ -1191,7 +1191,7 @@ function _element_validate_integer($element, &$form_state) {
 function _element_validate_integer_positive($element, &$form_state) {
   $value = $element['#value'];
   if ($value !== '' && (!is_numeric($value) || intval($value) != $value || $value <= 0)) {
-    form_error($element, t('%name must be a positive integer.', array('%name' => $element['#title'])));
+    form_error($element, $form_state, t('%name must be a positive integer.', array('%name' => $element['#title'])));
   }
 }
 
@@ -1201,6 +1201,6 @@ function _element_validate_integer_positive($element, &$form_state) {
 function _element_validate_number($element, &$form_state) {
   $value = $element['#value'];
   if ($value != '' && !is_numeric($value)) {
-    form_error($element, t('%name must be a number.', array('%name' => $element['#title'])));
+    form_error($element, $form_state, t('%name must be a number.', array('%name' => $element['#title'])));
   }
 }
diff --git a/modules/field/modules/list/list.module b/modules/field/modules/list/list.module
index 608679b..288ec9c 100644
--- a/modules/field/modules/list/list.module
+++ b/modules/field/modules/list/list.module
@@ -166,21 +166,21 @@ function list_allowed_values_setting_validate($element, &$form_state) {
   $values = list_extract_allowed_values($element['#value'], $field['type'], $generate_keys);
 
   if (!is_array($values)) {
-    form_error($element, t('Allowed values list: invalid input.'));
+    form_error($element, $form_state, t('Allowed values list: invalid input.'));
   }
   else {
     // Check that keys are valid for the field type.
     foreach ($values as $key => $value) {
       if ($field_type == 'list_integer' && !preg_match('/^-?\d+$/', $key)) {
-        form_error($element, t('Allowed values list: keys must be integers.'));
+        form_error($element, $form_state, t('Allowed values list: keys must be integers.'));
         break;
       }
       if ($field_type == 'list_float' && !is_numeric($key)) {
-        form_error($element, t('Allowed values list: each key must be a valid integer or decimal.'));
+        form_error($element, $form_state, t('Allowed values list: each key must be a valid integer or decimal.'));
         break;
       }
       elseif ($field_type == 'list_text' && drupal_strlen($key) > 255) {
-        form_error($element, t('Allowed values list: each key must be a string at most 255 characters long.'));
+        form_error($element, $form_state, t('Allowed values list: each key must be a string at most 255 characters long.'));
         break;
       }
     }
@@ -189,7 +189,7 @@ function list_allowed_values_setting_validate($element, &$form_state) {
     if ($has_data) {
       $lost_keys = array_diff(array_keys($field['settings']['allowed_values']), array_keys($values));
       if (_list_values_in_use($field, $lost_keys)) {
-        form_error($element, t('Allowed values list: some values are being removed while currently in use.'));
+        form_error($element, $form_state, t('Allowed values list: some values are being removed while currently in use.'));
       }
     }
 
diff --git a/modules/field/modules/number/number.module b/modules/field/modules/number/number.module
index 20e3807..f76dd6a 100644
--- a/modules/field/modules/number/number.module
+++ b/modules/field/modules/number/number.module
@@ -376,7 +376,7 @@ function number_field_widget_validate($element, &$form_state) {
         break;
     }
     if ($value != preg_replace($regexp, '', $value)) {
-      form_error($element, $message);
+      form_error($element, $form_state, $message);
     }
     else {
       // Substitute the decimal separator,
@@ -392,5 +392,5 @@ function number_field_widget_validate($element, &$form_state) {
  * Implements hook_field_widget_error().
  */
 function number_field_widget_error($element, $error, $form, &$form_state) {
-  form_error($element['value'], $error['message']);
+  form_error($element['value'], $form_state, $error['message']);
 }
diff --git a/modules/field/modules/options/options.module b/modules/field/modules/options/options.module
index 385f3f4..bedc6b2 100644
--- a/modules/field/modules/options/options.module
+++ b/modules/field/modules/options/options.module
@@ -159,7 +159,7 @@ function options_field_widget_settings_form($field, $instance) {
  */
 function options_field_widget_validate($element, &$form_state) {
   if ($element['#required'] && $element['#value'] == '_none') {
-    form_error($element, t('!name field is required.', array('!name' => $element['#title'])));
+    form_error($element, $form_state, t('!name field is required.', array('!name' => $element['#title'])));
   }
   // Transpose selections from field => delta to delta => field, turning
   // multiple selected options into multiple parent elements.
@@ -373,7 +373,7 @@ function options_array_flatten($array) {
  * Implements hook_field_widget_error().
  */
 function options_field_widget_error($element, $error, $form, &$form_state) {
-  form_error($element, $error['message']);
+  form_error($element, $form_state, $error['message']);
 }
 
 /**
diff --git a/modules/field/modules/text/text.module b/modules/field/modules/text/text.module
index 89c605c..097c63c 100644
--- a/modules/field/modules/text/text.module
+++ b/modules/field/modules/text/text.module
@@ -575,7 +575,7 @@ function text_field_widget_error($element, $error, $form, &$form_state) {
       break;
   }
 
-  form_error($error_element, $error['message']);
+  form_error($error_element, $form_state, $error['message']);
 }
 
 /**
diff --git a/modules/field/tests/field_test.field.inc b/modules/field/tests/field_test.field.inc
index b8a2939..594e366 100644
--- a/modules/field/tests/field_test.field.inc
+++ b/modules/field/tests/field_test.field.inc
@@ -204,7 +204,7 @@ function field_test_field_widget_error($element, $error, $form, &$form_state) {
     $error_element = $element;
   }
 
-  form_error($error_element, $error['message']);
+  form_error($error_element, $form_state, $error['message']);
 }
 
 /**
diff --git a/modules/field_ui/field_ui.admin.inc b/modules/field_ui/field_ui.admin.inc
index 96beb13..06f3a5a 100644
--- a/modules/field_ui/field_ui.admin.inc
+++ b/modules/field_ui/field_ui.admin.inc
@@ -628,12 +628,12 @@ function _field_ui_field_overview_form_validate_add_new($form, &$form_state) {
   if (array_filter(array($field['label'], $field['field_name'], $field['type'], $field['widget_type']))) {
     // Missing label.
     if (!$field['label']) {
-      form_set_error('fields][_add_new_field][label', t('Add new field: you need to provide a label.'));
+      form_error($form['fields']['_add_new_field']['label'], $form_state, t('Add new field: you need to provide a label.'));
     }
 
     // Missing field name.
     if (!$field['field_name']) {
-      form_set_error('fields][_add_new_field][field_name', t('Add new field: you need to provide a field name.'));
+      form_error($form['fields']['_add_new_field']['field_name'], $form_state, t('Add new field: you need to provide a field name.'));
     }
     // Field name validation.
     else {
@@ -647,34 +647,34 @@ function _field_ui_field_overview_form_validate_add_new($form, &$form_state) {
 
       // Invalid field name.
       if (!preg_match('!^field_[a-z0-9_]+$!', $field_name)) {
-        form_set_error('fields][_add_new_field][field_name', t('Add new field: the field name %field_name is invalid. The name must include only lowercase unaccentuated letters, numbers, and underscores.', array('%field_name' => $field_name)));
+        form_error($form['fields']['_add_new_field']['field_name'], $form_state, t('Add new field: the field name %field_name is invalid. The name must include only lowercase unaccentuated letters, numbers, and underscores.', array('%field_name' => $field_name)));
       }
       if (strlen($field_name) > 32) {
-        form_set_error('fields][_add_new_field][field_name', t("Add new field: the field name %field_name is too long. The name is limited to 32 characters, including the 'field_' prefix.", array('%field_name' => $field_name)));
+        form_error($form['fields']['_add_new_field']['field_name'], $form_state, t("Add new field: the field name %field_name is too long. The name is limited to 32 characters, including the 'field_' prefix.", array('%field_name' => $field_name)));
       }
 
       // Field name already exists. We need to check inactive fields as well, so
       // we can't use field_info_fields().
       $fields = field_read_fields(array('field_name' => $field_name), array('include_inactive' => TRUE));
       if ($fields) {
-        form_set_error('fields][_add_new_field][field_name', t('Add new field: the field name %field_name already exists.', array('%field_name' => $field_name)));
+        form_error($form['fields']['_add_new_field']['field_name'], $form_state, t('Add new field: the field name %field_name already exists.', array('%field_name' => $field_name)));
       }
     }
 
     // Missing field type.
     if (!$field['type']) {
-      form_set_error('fields][_add_new_field][type', t('Add new field: you need to select a field type.'));
+      form_error($form['fields']['_add_new_field']['type'], $form_state, t('Add new field: you need to select a field type.'));
     }
 
     // Missing widget type.
     if (!$field['widget_type']) {
-      form_set_error('fields][_add_new_field][widget_type', t('Add new field: you need to select a widget.'));
+      form_error($form['fields']['_add_new_field']['widget_type'], $form_state, t('Add new field: you need to select a widget.'));
     }
     // Wrong widget type.
     elseif ($field['type']) {
       $widget_types = field_ui_widget_type_options($field['type']);
       if (!isset($widget_types[$field['widget_type']])) {
-        form_set_error('fields][_add_new_field][widget_type', t('Add new field: invalid widget.'));
+        form_error($form['fields']['_add_new_field']['widget_type'], $form_state, t('Add new field: invalid widget.'));
       }
     }
   }
@@ -695,23 +695,23 @@ function _field_ui_field_overview_form_validate_add_existing($form, &$form_state
     if (array_filter(array($field['label'], $field['field_name'], $field['widget_type']))) {
       // Missing label.
       if (!$field['label']) {
-        form_set_error('fields][_add_existing_field][label', t('Add existing field: you need to provide a label.'));
+        form_error($form['fields']['_add_existing_field']['label'], $form_state, t('Add existing field: you need to provide a label.'));
       }
 
       // Missing existing field name.
       if (!$field['field_name']) {
-        form_set_error('fields][_add_existing_field][field_name', t('Add existing field: you need to select a field.'));
+        form_error($form['fields']['_add_existing_field']['field_name'], $form_state, t('Add existing field: you need to select a field.'));
       }
 
       // Missing widget type.
       if (!$field['widget_type']) {
-        form_set_error('fields][_add_existing_field][widget_type', t('Add existing field: you need to select a widget.'));
+        form_error($form['fields']['_add_existing_field']['widget_type'], $form_state, t('Add existing field: you need to select a widget.'));
       }
       // Wrong widget type.
       elseif ($field['field_name'] && ($existing_field = field_info_field($field['field_name']))) {
         $widget_types = field_ui_widget_type_options($existing_field['type']);
         if (!isset($widget_types[$field['widget_type']])) {
-          form_set_error('fields][_add_existing_field][widget_type', t('Add existing field: invalid widget.'));
+          form_error($form['fields']['_add_existing_field']['widget_type'], $form_state, t('Add existing field: invalid widget.'));
         }
       }
     }
diff --git a/modules/file/file.field.inc b/modules/file/file.field.inc
index 2af3cb6..e4c54d5 100644
--- a/modules/file/file.field.inc
+++ b/modules/file/file.field.inc
@@ -126,7 +126,7 @@ function file_field_instance_settings_form($field, $instance) {
  */
 function _file_generic_settings_max_filesize($element, &$form_state) {
   if (!empty($element['#value']) && !is_numeric(parse_size($element['#value']))) {
-    form_error($element, t('The "!name" option must contain a valid value. You may either leave the text field empty or enter a string like "512" (bytes), "80 KB" (kilobytes) or "50 MB" (megabytes).', array('!name' => t($element['title']))));
+    form_error($element, $form_state, t('The "!name" option must contain a valid value. You may either leave the text field empty or enter a string like "512" (bytes), "80 KB" (kilobytes) or "50 MB" (megabytes).', array('!name' => t($element['title']))));
   }
 }
 
@@ -143,7 +143,7 @@ function _file_generic_settings_extensions($element, &$form_state) {
     $extensions = array_filter(explode(' ', $extensions));
     $extensions = implode(' ', array_unique($extensions));
     if (!preg_match('/^([a-z0-9]+([.][a-z0-9])* ?)+$/', $extensions)) {
-      form_error($element, t('The list of allowed extensions is not valid, be sure to exclude leading dots and to separate extensions with a comma or space.'));
+      form_error($element, $form_state, t('The list of allowed extensions is not valid, be sure to exclude leading dots and to separate extensions with a comma or space.'));
     }
     else {
       form_set_value($element, $extensions, $form_state);
@@ -586,7 +586,7 @@ function file_field_widget_uri($field, $instance, $data = array()) {
 /**
  * The #value_callback for the file_generic field element.
  */
-function file_field_widget_value($element, $input = FALSE, $form_state) {
+function file_field_widget_value($element, $input = FALSE, &$form_state) {
   if ($input) {
     // Checkboxes lose their value when empty.
     // If the display field is present make sure its unchecked value is saved.
diff --git a/modules/file/file.module b/modules/file/file.module
index 4002701..a91a773 100644
--- a/modules/file/file.module
+++ b/modules/file/file.module
@@ -463,7 +463,7 @@ function file_managed_file_process($element, &$form_state, $form) {
 /**
  * The #value_callback for a managed_file type element.
  */
-function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL) {
+function file_managed_file_value(&$element, $input = FALSE, &$form_state) {
   $fid = 0;
 
   // Find the current value of this field from the form state.
@@ -484,7 +484,7 @@ function file_managed_file_value(&$element, $input = FALSE, $form_state = NULL)
     $return = $input;
 
     // Uploads take priority over all other values.
-    if ($file = file_managed_file_save_upload($element)) {
+    if ($file = file_managed_file_save_upload($element, $form_state)) {
       $fid = $file->fid;
     }
     else {
@@ -539,18 +539,18 @@ function file_managed_file_validate(&$element, &$form_state) {
       if ($file->status == FILE_STATUS_PERMANENT) {
         $references = file_usage_list($file);
         if (empty($references)) {
-          form_error($element, t('The file used in the !name field may not be referenced.', array('!name' => $element['#title'])));
+          form_error($element, $form_state, t('The file used in the !name field may not be referenced.', array('!name' => $element['#title'])));
         }
       }
     }
     else {
-      form_error($element, t('The file referenced by the !name field does not exist.', array('!name' => $element['#title'])));
+      form_error($element, $form_state, t('The file referenced by the !name field does not exist.', array('!name' => $element['#title'])));
     }
   }
 
   // Check required property based on the FID.
   if ($element['#required'] && empty($element['fid']['#value']) && !in_array($clicked_button, array('upload_button', 'remove_button'))) {
-    form_error($element['upload'], t('!name field is required.', array('!name' => $element['#title'])));
+    form_error($element['upload'], $form_state, t('!name field is required.', array('!name' => $element['#title'])));
   }
 
   // Consolidate the array value of this field to a single FID.
@@ -609,7 +609,7 @@ function file_managed_file_submit($form, &$form_state) {
  *   The file object representing the file that was saved, or FALSE if no file
  *   was saved.
  */
-function file_managed_file_save_upload($element) {
+function file_managed_file_save_upload($element, &$form_state) {
   $upload_name = implode('_', $element['#parents']);
   if (empty($_FILES['files']['name'][$upload_name])) {
     return FALSE;
@@ -618,13 +618,13 @@ function file_managed_file_save_upload($element) {
   $destination = isset($element['#upload_location']) ? $element['#upload_location'] : NULL;
   if (isset($destination) && !file_prepare_directory($destination, FILE_CREATE_DIRECTORY)) {
     watchdog('file', 'The upload directory %directory for the file field !name could not be created or is not accessible. A newly uploaded file could not be saved in this directory as a consequence, and the upload was canceled.', array('%directory' => $destination, '!name' => $element['#field_name']));
-    form_set_error($upload_name, t('The file could not be uploaded.'));
+    form_error($element, $form_state, t('The file could not be uploaded.'));
     return FALSE;
   }
 
   if (!$file = file_save_upload($upload_name, $element['#upload_validators'], $destination)) {
     watchdog('file', 'The file upload failed. %upload', array('%upload' => $upload_name));
-    form_set_error($upload_name, t('The file in the !name field was unable to be uploaded.', array('!name' => $element['#title'])));
+    form_error($element, $form_state, t('The file in the !name field was unable to be uploaded.', array('!name' => $element['#title'])));
     return FALSE;
   }
 
diff --git a/modules/filter/filter.admin.inc b/modules/filter/filter.admin.inc
index 5a21e6e..420ab46 100644
--- a/modules/filter/filter.admin.inc
+++ b/modules/filter/filter.admin.inc
@@ -299,7 +299,7 @@ function filter_admin_format_form_validate($form, &$form_state) {
 
   $result = db_query("SELECT format FROM {filter_format} WHERE name = :name AND format <> :format", array(':name' => $format_name, ':format' => $format_format))->fetchField();
   if ($result) {
-    form_set_error('name', t('Text format names must be unique. A format named %name already exists.', array('%name' => $format_name)));
+    form_error($form['name'], $form_state, t('Text format names must be unique. A format named %name already exists.', array('%name' => $format_name)));
   }
 }
 
diff --git a/modules/forum/forum.module b/modules/forum/forum.module
index c58b5c9..2ad7a18 100644
--- a/modules/forum/forum.module
+++ b/modules/forum/forum.module
@@ -285,7 +285,7 @@ function forum_node_view($node, $view_mode) {
  *
  * Check in particular that only a "leaf" term in the associated taxonomy.
  */
-function forum_node_validate($node, $form) {
+function forum_node_validate($node, $form, &$form_state) {
   if (_forum_node_check_node_type($node)) {
     $langcode = $form['taxonomy_forums']['#language'];
     // vocabulary is selected, not a "container" term.
@@ -301,7 +301,7 @@ function forum_node_validate($node, $form) {
         }
         $term = taxonomy_term_load($item['tid']);
         if (!$term) {
-          form_set_error('taxonomy_forums', t('Select a forum.'));
+          form_error($form['taxonomy_forums'], $form_state, t('Select a forum.'));
           continue;
         }
         $used = db_query_range('SELECT 1 FROM {taxonomy_term_data} WHERE tid = :tid AND vid = :vid',0 , 1, array(
@@ -309,7 +309,7 @@ function forum_node_validate($node, $form) {
           ':vid' => $term->vid,
         ))->fetchField();
         if ($used && in_array($term->tid, $containers)) {
-          form_set_error('taxonomy_forums', t('The item %forum is a forum container, not a forum. Select one of the forums below instead.', array('%forum' => $term->name)));
+          form_error($form['taxonomy_forums'], $form_state, t('The item %forum is a forum container, not a forum. Select one of the forums below instead.', array('%forum' => $term->name)));
         }
       }
     }
diff --git a/modules/image/image.admin.inc b/modules/image/image.admin.inc
index d72fdf4..bddbc93 100644
--- a/modules/image/image.admin.inc
+++ b/modules/image/image.admin.inc
@@ -169,7 +169,7 @@ function image_style_form($form, &$form_state, $style) {
  */
 function image_style_form_add_validate($form, &$form_state) {
   if (!$form_state['values']['new']) {
-    form_error($form['effects']['new']['new'], t('Select an effect to add.'));
+    form_error($form['effects']['new']['new'], $form_state, t('Select an effect to add.'));
   }
 }
 
@@ -270,16 +270,16 @@ function image_style_add_form_submit($form, &$form_state) {
 /**
  * Element validate function to ensure unique, URL safe style names.
  */
-function image_style_name_validate($element, $form_state) {
+function image_style_name_validate($element, &$form_state) {
   // Check for duplicates.
   $styles = image_styles();
   if (isset($styles[$element['#value']]) && (!isset($form_state['image_style']['isid']) || $styles[$element['#value']]['isid'] != $form_state['image_style']['isid'])) {
-    form_set_error($element['#name'], t('The image style name %name is already in use.', array('%name' => $element['#value'])));
+    form_error($element, $form_state, t('The image style name %name is already in use.', array('%name' => $element['#value'])));
   }
 
   // Check for illegal characters in image style names.
   if (preg_match('/[^0-9a-z_\-]/', $element['#value'])) {
-    form_set_error($element['#name'], t('Please only use lowercase alphanumeric characters, underscores (_), and hyphens (-) for style names.'));
+    form_error($element, $form_state, t('Please only use lowercase alphanumeric characters, underscores (_), and hyphens (-) for style names.'));
   }
 }
 
@@ -464,10 +464,10 @@ function image_effect_integer_validate($element, &$form_state) {
   $value = empty($element['#allow_negative']) ? $element['#value'] : preg_replace('/^-/', '', $element['#value']);
   if ($element['#value'] != '' && (!is_numeric($value) || intval($value) <= 0)) {
     if (empty($element['#allow_negative'])) {
-      form_error($element, t('!name must be an integer.', array('!name' => $element['#title'])));
+      form_error($element, $form_state, t('!name must be an integer.', array('!name' => $element['#title'])));
     }
     else {
-      form_error($element, t('!name must be a positive integer.', array('!name' => $element['#title'])));
+      form_error($element, $form_state, t('!name must be a positive integer.', array('!name' => $element['#title'])));
     }
   }
 }
@@ -479,7 +479,7 @@ function image_effect_color_validate($element, &$form_state) {
   if ($element['#value'] != '') {
     $hex_value = preg_replace('/^#/', '', $element['#value']);
     if (!preg_match('/^#[0-9A-F]{3}([0-9A-F]{3})?$/', $element['#value'])) {
-      form_error($element, t('!name must be a hexadecimal color value.', array('!name' => $element['#title'])));
+      form_error($element, $form_state, t('!name must be a hexadecimal color value.', array('!name' => $element['#title'])));
     }
   }
 }
@@ -490,7 +490,7 @@ function image_effect_color_validate($element, &$form_state) {
  */
 function image_effect_scale_validate($element, &$form_state) {
   if (empty($element['width']['#value']) && empty($element['height']['#value'])) {
-    form_error($element, t('Width and height can not both be blank.'));
+    form_error($element, $form_state, t('Width and height can not both be blank.'));
   }
 }
 
diff --git a/modules/image/image.field.inc b/modules/image/image.field.inc
index 07cc1e0..28dfc1e 100644
--- a/modules/image/image.field.inc
+++ b/modules/image/image.field.inc
@@ -161,11 +161,11 @@ function _image_field_resolution_validate($element, &$form_state) {
     foreach (array('x', 'y') as $dimension) {
       $value = $element[$dimension]['#value'];
       if (!is_numeric($value)) {
-        form_error($element[$dimension], t('Height and width values must be numeric.'));
+        form_error($element[$dimension], $form_state, t('Height and width values must be numeric.'));
         return;
       }
       if (intval($value) == 0) {
-        form_error($element[$dimension], t('Both a height and width value must be specified in the !name field.', array('!name' => $element['#title'])));
+        form_error($element[$dimension], $form_state, t('Both a height and width value must be specified in the !name field.', array('!name' => $element['#title'])));
         return;
       }
     }
diff --git a/modules/locale/locale.admin.inc b/modules/locale/locale.admin.inc
index dff6537..0a44a5c 100644
--- a/modules/locale/locale.admin.inc
+++ b/modules/locale/locale.admin.inc
@@ -316,7 +316,7 @@ function locale_languages_predefined_form_validate($form, &$form_state) {
   $langcode = $form_state['values']['langcode'];
 
   if (($duplicate = db_query("SELECT COUNT(*) FROM {languages} WHERE language = :language", array(':language' => $langcode))->fetchField()) != 0) {
-    form_set_error('langcode', t('The language %language (%code) already exists.', array('%language' => $form_state['values']['name'], '%code' => $langcode)));
+    form_error($form['langcode'], $form_state, t('The language %language (%code) already exists.', array('%language' => $form_state['values']['name'], '%code' => $langcode)));
   }
 
   if (!isset($form_state['values']['name'])) {
@@ -324,7 +324,7 @@ function locale_languages_predefined_form_validate($form, &$form_state) {
     include_once DRUPAL_ROOT . '/includes/iso.inc';
     $predefined = _locale_get_predefined_list();
     if (!isset($predefined[$langcode])) {
-      form_set_error('langcode', t('Invalid language code.'));
+      form_error($form['langcode'], $form_state, t('Invalid language code.'));
     }
   }
   else {
@@ -366,26 +366,26 @@ function locale_languages_predefined_form_submit($form, &$form_state) {
 function locale_languages_edit_form_validate($form, &$form_state) {
   // Ensure sane field values for langcode, name, and native.
   if (!isset($form['langcode_view']) && preg_match('@[^a-zA-Z_-]@', $form_state['values']['langcode'])) {
-    form_set_error('langcode', t('%field may only contain characters a-z, underscores, or hyphens.', array('%field' => $form['langcode']['#title'])));
+    form_error($form['langcode'], $form_state, t('%field may only contain characters a-z, underscores, or hyphens.', array('%field' => $form['langcode']['#title'])));
   }
   if ($form_state['values']['name'] != check_plain($form_state['values']['name'])) {
-    form_set_error('name', t('%field cannot contain any markup.', array('%field' => $form['name']['#title'])));
+    form_error($form['name'], $form_state, t('%field cannot contain any markup.', array('%field' => $form['name']['#title'])));
   }
   if ($form_state['values']['native'] != check_plain($form_state['values']['native'])) {
-    form_set_error('native', t('%field cannot contain any markup.', array('%field' => $form['native']['#title'])));
+    form_error($form['native'], $form_state, t('%field cannot contain any markup.', array('%field' => $form['native']['#title'])));
   }
 
   if (!empty($form_state['values']['domain']) && !empty($form_state['values']['prefix'])) {
-    form_set_error('prefix', t('Domain and path prefix values should not be set at the same time.'));
+    form_error($form['prefix'], $form_state, t('Domain and path prefix values should not be set at the same time.'));
   }
   if (!empty($form_state['values']['domain']) && $duplicate = db_query("SELECT language FROM {languages} WHERE domain = :domain AND language <> :language", array(':domain' => $form_state['values']['domain'], ':language' => $form_state['values']['langcode']))->fetchField()) {
-    form_set_error('domain', t('The domain (%domain) is already tied to a language (%language).', array('%domain' => $form_state['values']['domain'], '%language' => $duplicate->language)));
+    form_error($form['domain'], $form_state, t('The domain (%domain) is already tied to a language (%language).', array('%domain' => $form_state['values']['domain'], '%language' => $duplicate->language)));
   }
   if (empty($form_state['values']['prefix']) && language_default('language') != $form_state['values']['langcode'] && empty($form_state['values']['domain'])) {
-    form_set_error('prefix', t('Only the default language can have both the domain and prefix empty.'));
+    form_error($form['prefix'], $form_state, t('Only the default language can have both the domain and prefix empty.'));
   }
   if (!empty($form_state['values']['prefix']) && $duplicate = db_query("SELECT language FROM {languages} WHERE prefix = :prefix AND language <> :language", array(':prefix' => $form_state['values']['prefix'], ':language' => $form_state['values']['langcode']))->fetchField()) {
-    form_set_error('prefix', t('The prefix (%prefix) is already tied to a language (%language).', array('%prefix' => $form_state['values']['prefix'], '%language' => $duplicate->language)));
+    form_error($form['prefix'], $form_state, t('The prefix (%prefix) is already tied to a language (%language).', array('%prefix' => $form_state['values']['prefix'], '%language' => $duplicate->language)));
   }
 }
 
@@ -887,7 +887,7 @@ function locale_translation_filter_form() {
  */
 function locale_translation_filter_form_validate($form, &$form_state) {
   if ($form_state['values']['op'] == t('Filter') && empty($form_state['values']['language']) && empty($form_state['values']['group'])) {
-    form_set_error('type', t('You must select something to filter by.'));
+    form_error($form['type'], $form_state, t('You must select something to filter by.'));
   }
 }
 
@@ -1168,7 +1168,7 @@ function locale_translate_edit_form_validate($form, &$form_state) {
   $safe_check_needed = $form_state['values']['textgroup'] == 'default';
   foreach ($form_state['values']['translations'] as $key => $value) {
     if ($safe_check_needed && !locale_string_is_safe($value)) {
-      form_set_error('translations', t('The submitted string contains disallowed HTML: %string', array('%string' => $value)));
+      form_error($form['translations'], $form_state, t('The submitted string contains disallowed HTML: %string', array('%string' => $value)));
       watchdog('locale', 'Attempted submission of a translation string with disallowed HTML: %string', array('%string' => $value), LOG_WARNING);
     }
   }
diff --git a/modules/menu/menu.admin.inc b/modules/menu/menu.admin.inc
index ec073e5..7e284ca 100644
--- a/modules/menu/menu.admin.inc
+++ b/modules/menu/menu.admin.inc
@@ -382,7 +382,7 @@ function menu_edit_item_validate($form, &$form_state) {
     }
   }
   if (!trim($item['link_path']) || !drupal_valid_path($item['link_path'], TRUE)) {
-    form_set_error('link_path', t("The path '@link_path' is either invalid or you do not have access to it.", array('@link_path' => $item['link_path'])));
+    form_error($form['link_path'], $form_state, t("The path '@link_path' is either invalid or you do not have access to it.", array('@link_path' => $item['link_path'])));
   }
 }
 
diff --git a/modules/node/content_types.inc b/modules/node/content_types.inc
index cadabca..e78e3dd 100644
--- a/modules/node/content_types.inc
+++ b/modules/node/content_types.inc
@@ -267,14 +267,14 @@ function node_type_form_validate($form, &$form_state) {
     // 'theme' conflicts with theme_node_form().
     // '0' is invalid, since elsewhere we check it using empty().
     if (in_array($type->type, array('0', 'theme'))) {
-      form_set_error('type', t("Invalid machine-readable name. Enter a name other than %invalid.", array('%invalid' => $type->type)));
+      form_error($form['type'], $form_state, t("Invalid machine-readable name. Enter a name other than %invalid.", array('%invalid' => $type->type)));
     }
   }
 
   $names = array_flip($types);
 
   if (isset($names[$type->name]) && $names[$type->name] != $old_type) {
-    form_set_error('name', t('The human-readable name %name is already taken.', array('%name' => $type->name)));
+    form_error($form['name'], $form_state, t('The human-readable name %name is already taken.', array('%name' => $type->name)));
   }
 }
 
diff --git a/modules/node/node.admin.inc b/modules/node/node.admin.inc
index a6ea1b5..9059941 100644
--- a/modules/node/node.admin.inc
+++ b/modules/node/node.admin.inc
@@ -533,7 +533,7 @@ function node_admin_nodes() {
 function node_admin_nodes_validate($form, &$form_state) {
   // Error if there are no items to select.
   if (!is_array($form_state['values']['nodes']) || !count(array_filter($form_state['values']['nodes']))) {
-    form_set_error('', t('No items selected.'));
+    form_error($form['nodes'], $form_state, t('No items selected.'));
   }
 }
 
diff --git a/modules/node/node.api.php b/modules/node/node.api.php
index d7a98c6..19be194 100644
--- a/modules/node/node.api.php
+++ b/modules/node/node.api.php
@@ -722,7 +722,7 @@ function hook_node_update_index($node) {
  * end of all the standard validation steps, and after the type-specific
  * hook_validate() is invoked.
  *
- * To indicate a validation error, use form_set_error().
+ * To indicate a validation error, use form_error().
  *
  * Note: Changes made to the $node object within your hook implementation will
  * have no effect.  The preferred method to change a node's content is to use
@@ -741,7 +741,7 @@ function hook_node_update_index($node) {
 function hook_node_validate($node, $form, &$form_state) {
   if (isset($node->end) && isset($node->start)) {
     if ($node->start > $node->end) {
-      form_set_error('time', t('An event may not end before it starts.'));
+      form_error($form['time'], $form_state, t('An event may not end before it starts.'));
     }
   }
 }
@@ -1198,7 +1198,7 @@ function hook_update($node) {
  * of all the standard validation steps, and before hook_node_validate() is
  * invoked.
  *
- * To indicate a validation error, use form_set_error().
+ * To indicate a validation error, use form_error().
  *
  * Note: Changes made to the $node object within your hook implementation will
  * have no effect.  The preferred method to change a node's content is to use
@@ -1216,7 +1216,7 @@ function hook_update($node) {
 function hook_validate($node, $form, &$form_state) {
   if (isset($node->end) && isset($node->start)) {
     if ($node->start > $node->end) {
-      form_set_error('time', t('An event may not end before it starts.'));
+      form_error($form['time'], $form_state, t('An event may not end before it starts.'));
     }
   }
 }
diff --git a/modules/node/node.module b/modules/node/node.module
index 2dd1926..02837d4 100644
--- a/modules/node/node.module
+++ b/modules/node/node.module
@@ -969,7 +969,7 @@ function node_validate($node, $form, &$form_state) {
   $type = node_type_get_type($node);
 
   if (isset($node->nid) && (node_last_changed($node->nid) > $node->changed)) {
-    form_set_error('changed', t('The content on this page has either been modified by another user, or you have already submitted modifications using this form. As a result, your changes cannot be saved.'));
+    form_error($form['changed'], $form_state, t('The content on this page has either been modified by another user, or you have already submitted modifications using this form. As a result, your changes cannot be saved.'));
   }
 
   // Validate the "authored by" field.
@@ -977,12 +977,12 @@ function node_validate($node, $form, &$form_state) {
     // The use of empty() is mandatory in the context of usernames
     // as the empty string denotes the anonymous user. In case we
     // are dealing with an anonymous user we set the user ID to 0.
-    form_set_error('name', t('The username %name does not exist.', array('%name' => $node->name)));
+    form_error($form['name'], $form_state, t('The username %name does not exist.', array('%name' => $node->name)));
   }
 
   // Validate the "authored on" field.
   if (!empty($node->date) && strtotime($node->date) === FALSE) {
-    form_set_error('date', t('You have to specify a valid date.'));
+    form_error($form['date'], $form_state, t('You have to specify a valid date.'));
   }
 
   // Invoke hook_validate() for node type specific validation and
@@ -3734,10 +3734,10 @@ function node_assign_owner_action_form($context) {
 /**
  * Validates settings form for node_assign_owner_action().
  */
-function node_assign_owner_action_validate($form, $form_state) {
+function node_assign_owner_action_validate($form, &$form_state) {
   $exists = (bool) db_query_range('SELECT 1 FROM {users} WHERE name = :name', 0, 1, array(':name' => $form_state['values']['owner_name']))->fetchField();
   if (!$exists) {
-    form_set_error('owner_name', t('Enter a valid username.'));
+    form_error($form['owner_name'], $form_state, t('Enter a valid username.'));
   }
 }
 
diff --git a/modules/node/node.pages.inc b/modules/node/node.pages.inc
index 5879963..7047093 100644
--- a/modules/node/node.pages.inc
+++ b/modules/node/node.pages.inc
@@ -267,7 +267,7 @@ function node_form($form, &$form_state, $node) {
   $form['actions'] = array('#type' => 'actions');
   $form['actions']['submit'] = array(
     '#type' => 'submit',
-    '#access' => variable_get('node_preview_' . $node->type, DRUPAL_OPTIONAL) != DRUPAL_REQUIRED || (!form_get_errors() && isset($form_state['node_preview'])),
+    '#access' => variable_get('node_preview_' . $node->type, DRUPAL_OPTIONAL) != DRUPAL_REQUIRED || (!form_get_errors($form_state) && isset($form_state['node_preview'])),
     '#value' => t('Save'),
     '#weight' => 5,
     '#submit' => array('node_form_submit'),
diff --git a/modules/openid/openid.pages.inc b/modules/openid/openid.pages.inc
index 6e3f096..dc389e1 100644
--- a/modules/openid/openid.pages.inc
+++ b/modules/openid/openid.pages.inc
@@ -81,7 +81,7 @@ function openid_user_add_validate($form, &$form_state) {
   // Check for existing entries.
   $claimed_id = openid_normalize($form_state['values']['openid_identifier']);
   if (db_query("SELECT authname FROM {authmap} WHERE authname = :authname", (array(':authname' => $claimed_id)))->fetchField()) {
-    form_set_error('openid_identifier', t('That OpenID is already in use on this site.'));
+    form_error($form['openid_identifier'], $form_state, t('That OpenID is already in use on this site.'));
   }
 }
 
diff --git a/modules/path/path.admin.inc b/modules/path/path.admin.inc
index f10142b..f466b61 100644
--- a/modules/path/path.admin.inc
+++ b/modules/path/path.admin.inc
@@ -187,10 +187,10 @@ function path_admin_form_validate($form, &$form_state) {
     ->fetchField();
 
   if ($has_alias) {
-    form_set_error('alias', t('The alias %alias is already in use in this language.', array('%alias' => $alias)));
+    form_error($form['alias'], $form_state, t('The alias %alias is already in use in this language.', array('%alias' => $alias)));
   }
   if (!drupal_valid_path($source)) {
-    form_set_error('source', t("The path '@link_path' is either invalid or you do not have access to it.", array('@link_path' => $source)));
+    form_error($form['source'], $form_state, t("The path '@link_path' is either invalid or you do not have access to it.", array('@link_path' => $source)));
   }
 }
 
diff --git a/modules/path/path.module b/modules/path/path.module
index 332287d..b0ac04f 100644
--- a/modules/path/path.module
+++ b/modules/path/path.module
@@ -173,7 +173,7 @@ function path_form_element_validate($element, &$form_state, $complete_form) {
     $query->addExpression('1');
     $query->range(0, 1);
     if ($query->execute()->fetchField()) {
-      form_set_error('alias', t('The alias is already in use.'));
+      form_error($complete_form['alias'], $form_state, t('The alias is already in use.'));
     }
   }
 }
diff --git a/modules/poll/poll.module b/modules/poll/poll.module
index f45b8bd..e02507b 100644
--- a/modules/poll/poll.module
+++ b/modules/poll/poll.module
@@ -447,7 +447,7 @@ function poll_node_form_submit(&$form, &$form_state) {
 /**
  * Implements hook_validate().
  */
-function poll_validate($node, $form) {
+function poll_validate($node, $form, &$form_state) {
   if (isset($node->title)) {
     // Check for at least two options and validate amount of votes:
     $realchoices = 0;
@@ -458,12 +458,12 @@ function poll_validate($node, $form) {
         $realchoices++;
       }
       if (isset($choice['chvotes']) && $choice['chvotes'] < 0) {
-        form_set_error("choice][$i][chvotes", t('Negative values are not allowed.'));
+        form_error($form['choice'][$i]['chvotes'], $form_state, t('Negative values are not allowed.'));
       }
     }
 
     if ($realchoices < 2) {
-      form_set_error("choice][$realchoices][chtext", t('You must fill in at least two choices.'));
+      form_error($form['choice'][$realchoices]['chtext'], $form_state, t('You must fill in at least two choices.'));
     }
   }
 }
@@ -731,7 +731,7 @@ function poll_view_voting($form, &$form_state, $node, $block = FALSE) {
  */
 function poll_view_voting_validate($form, &$form_state) {
   if ($form_state['values']['choice'] == -1) {
-    form_set_error( 'choice', t('Your vote could not be recorded because you did not select any of the choices.'));
+    form_error($form['choice'], $form_state, t('Your vote could not be recorded because you did not select any of the choices.'));
   }
 }
 
diff --git a/modules/profile/profile.admin.inc b/modules/profile/profile.admin.inc
index 622a96c..a84b1c6 100644
--- a/modules/profile/profile.admin.inc
+++ b/modules/profile/profile.admin.inc
@@ -310,19 +310,19 @@ Unless you know what you are doing, it is highly recommended that you prefix the
 function profile_field_form_validate($form, &$form_state) {
   // Validate the 'field name':
   if (preg_match('/[^a-zA-Z0-9_-]/', $form_state['values']['name'])) {
-    form_set_error('name', t('The specified form name contains one or more illegal characters. Spaces or any other special characters except dash (-) and underscore (_) are not allowed.'));
+    form_error($form['name'], $form_state, t('The specified form name contains one or more illegal characters. Spaces or any other special characters except dash (-) and underscore (_) are not allowed.'));
   }
 
   $users_table = drupal_get_schema('users');
   if (!empty($users_table['fields'][$form_state['values']['name']])) {
-    form_set_error('name', t('The specified form name is reserved for use by Drupal.'));
+    form_error($form['name'], $form_state, t('The specified form name is reserved for use by Drupal.'));
   }
   // Validate the category:
   if (!$form_state['values']['category']) {
-    form_set_error('category', t('You must enter a category.'));
+    form_error($form['category'], $form_state, t('You must enter a category.'));
   }
   if (strtolower($form_state['values']['category']) == 'account') {
-    form_set_error('category', t('The specified category name is reserved for use by Drupal.'));
+    form_error($form['category'], $form_state, t('The specified category name is reserved for use by Drupal.'));
   }
   $query = db_select('profile_field');
   $query->fields('profile_field', array('fid'));
@@ -338,21 +338,21 @@ function profile_field_form_validate($form, &$form_state) {
     ->execute()
     ->fetchField();
   if ($title) {
-    form_set_error('title', t('The specified title is already in use.'));
+    form_error($form['title'], $form_state, t('The specified title is already in use.'));
   }
   $name = $query_name
     ->condition('name', $form_state['values']['name'])
     ->execute()
     ->fetchField();
   if ($name) {
-    form_set_error('name', t('The specified name is already in use.'));
+    form_error($form['name'], $form_state, t('The specified name is already in use.'));
   }
   if ($form_state['values']['visibility'] == PROFILE_HIDDEN) {
     if ($form_state['values']['required']) {
-      form_set_error('required', t('A hidden field cannot be required.'));
+      form_error($form['required'], $form_state, t('A hidden field cannot be required.'));
     }
     if ($form_state['values']['register']) {
-      form_set_error('register', t('A hidden field cannot be set to visible on the user registration form.'));
+      form_error($form['register'], $form_state, t('A hidden field cannot be set to visible on the user registration form.'));
     }
   }
 }
diff --git a/modules/profile/profile.module b/modules/profile/profile.module
index 2374fe8..9cfd0d8 100644
--- a/modules/profile/profile.module
+++ b/modules/profile/profile.module
@@ -485,11 +485,11 @@ function profile_user_form_validate($form, &$form_state) {
   foreach ($result as $field) {
     if (!empty($form_state['values'][$field->name])) {
       if ($field->type == 'url' && !valid_url($form_state['values'][$field->name], TRUE)) {
-        form_set_error($field->name, t('The value provided for %field is not a valid URL.', array('%field' => $field->title)));
+        form_error($form[$field->name], $form_state, t('The value provided for %field is not a valid URL.', array('%field' => $field->title)));
       }
     }
     elseif ($field->required && !user_access('administer users')) {
-      form_set_error($field->name, t('The field %field is required.', array('%field' => $field->title)));
+      form_error($form[$field->name], $form_state, t('The field %field is required.', array('%field' => $field->title)));
     }
   }
 }
diff --git a/modules/search/search.admin.inc b/modules/search/search.admin.inc
index afa02de..26629ef 100644
--- a/modules/search/search.admin.inc
+++ b/modules/search/search.admin.inc
@@ -147,7 +147,7 @@ function search_admin_settings_validate($form, &$form_state) {
     $new_modules = array_filter($form_state['values']['search_active_modules']);
     $default = $form_state['values']['search_default_module'];
     if (!in_array($default, $new_modules, TRUE)) {
-      form_set_error('search_default_module', t('Your default search module is not selected as an active module.'));
+      form_error($form['search_default_module'], $form_state, t('Your default search module is not selected as an active module.'));
     }
   }
 }
diff --git a/modules/search/search.module b/modules/search/search.module
index 518272a..82277db 100644
--- a/modules/search/search.module
+++ b/modules/search/search.module
@@ -967,7 +967,7 @@ function search_form($form, &$form_state, $action = '', $keys = '', $module = NU
 
   // Sanity check.
   if (!$module_info) {
-    form_set_error(NULL, t('Search is currently disabled.'), 'error');
+    form_error($form, $form_state, t('Search is currently disabled.'), 'error');
     return $form;
   }
 
@@ -1041,7 +1041,7 @@ function search_box_form_submit($form, &$form_state) {
   // "field is required" because the search keywords field has no title.
   // The error message would also complain about a missing #title field.)
   if ($form_state['values']['search_block_form'] == '') {
-    form_set_error('keys', t('Please enter some keywords.'));
+    form_error($form['keys'], $form_state, t('Please enter some keywords.'));
   }
 
   $form_id = $form['form_id']['#value'];
@@ -1050,7 +1050,7 @@ function search_box_form_submit($form, &$form_state) {
     $form_state['redirect'] = 'search/' . $info['path'] . '/' . trim($form_state['values'][$form_id]);
   }
   else {
-    form_set_error(NULL, t('Search is currently disabled.'), 'error');
+    form_error($form, $form_state, t('Search is currently disabled.'), 'error');
   }
 }
 
diff --git a/modules/search/search.pages.inc b/modules/search/search.pages.inc
index ebf8f60..85ace17 100644
--- a/modules/search/search.pages.inc
+++ b/modules/search/search.pages.inc
@@ -151,7 +151,7 @@ function search_form_validate($form, &$form_state) {
 function search_form_submit($form, &$form_state) {
   $keys = $form_state['values']['processed_keys'];
   if ($keys == '') {
-    form_set_error('keys', t('Please enter some keywords.'));
+    form_error($form['keys'], $form_state, t('Please enter some keywords.'));
     // Fall through to the form redirect.
   }
 
diff --git a/modules/shortcut/shortcut.admin.inc b/modules/shortcut/shortcut.admin.inc
index 9735d37..fdce144 100644
--- a/modules/shortcut/shortcut.admin.inc
+++ b/modules/shortcut/shortcut.admin.inc
@@ -110,11 +110,11 @@ function shortcut_set_switch_validate($form, &$form_state) {
   if ($form_state['values']['set'] == 'new') {
     // Check to prevent creating a shortcut set with an empty title.
     if (trim($form_state['values']['new']) == '') {
-      form_set_error('new', t('The new set name is required.'));
+      form_error($form['new'], $form_state, t('The new set name is required.'));
     }
     // Check to prevent a duplicate title.
     if (shortcut_set_title_exists($form_state['values']['new'])) {
-      form_set_error('new', t('The shortcut set %name already exists. Choose another name.', array('%name' => $form_state['values']['new'])));
+      form_error($form['new'], $form_state, t('The shortcut set %name already exists. Choose another name.', array('%name' => $form_state['values']['new'])));
     }
   }
 }
@@ -228,7 +228,7 @@ function shortcut_set_add_form($form, &$form_state) {
 function shortcut_set_add_form_validate($form, &$form_state) {
   // Check to prevent a duplicate title.
   if (shortcut_set_title_exists($form_state['values']['new'])) {
-    form_set_error('new', t('The shortcut set %name already exists. Choose another name.', array('%name' => $form_state['values']['new'])));
+    form_error($form['new'], $form_state, t('The shortcut set %name already exists. Choose another name.', array('%name' => $form_state['values']['new'])));
   }
 }
 
@@ -500,7 +500,7 @@ function _shortcut_link_form_elements($shortcut_link = NULL) {
  */
 function shortcut_link_edit_validate($form, &$form_state) {
   if (!shortcut_valid_link($form_state['values']['shortcut_link']['link_path'])) {
-    form_set_error('shortcut_link][link_path', t('The link must correspond to a valid path on the site.'));
+    form_error($form['shortcut_link']['link_path'], $form_state, t('The link must correspond to a valid path on the site.'));
   }
 }
 
@@ -620,7 +620,7 @@ function shortcut_set_edit_form_validate($form, &$form_state) {
   // Check to prevent a duplicate title, if the title was edited from its
   // original value.
   if ($form_state['values']['title'] != $form_state['values']['shortcut_set']->title && shortcut_set_title_exists($form_state['values']['title'])) {
-    form_set_error('title', t('The shortcut set %name already exists. Choose another name.', array('%name' => $form_state['values']['title'])));
+    form_error($form['title'], $form_state, t('The shortcut set %name already exists. Choose another name.', array('%name' => $form_state['values']['title'])));
   }
 }
 
diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test
index e7ae9de..5197616 100644
--- a/modules/simpletest/tests/form.test
+++ b/modules/simpletest/tests/form.test
@@ -25,7 +25,7 @@ class FormsTestCase extends DrupalWebTestCase {
    * Carriage returns, tabs, spaces, and unchecked checkbox elements are not
    * valid content for a required field.
    *
-   * If the form field is found in form_get_errors() then the test pass.
+   * If the form field is found in $form_state['errors'] then the test pass.
    */
   function testRequiredFields() {
     // Originates from http://drupal.org/node/117748
@@ -74,7 +74,6 @@ class FormsTestCase extends DrupalWebTestCase {
           $form_id = $this->randomName();
           $form = array();
           $form_state = form_state_defaults();
-          form_clear_error();
           $form['op'] = array('#type' => 'submit', '#value' => t('Submit'));
           $element = $data['element']['#title'];
           $form[$element] = $data['element'];
@@ -84,7 +83,7 @@ class FormsTestCase extends DrupalWebTestCase {
           $form_state['method'] = 'post';
           drupal_prepare_form($form_id, $form, $form_state);
           drupal_process_form($form_id, $form, $form_state);
-          $errors = form_get_errors();
+          $errors = form_get_errors($form_state);
           // Form elements of type 'radios' throw all sorts of PHP notices
           // when you try to render them like this, so we ignore those for
           // testing the required marker.
@@ -521,7 +520,7 @@ class FormValidationTestCase extends DrupalWebTestCase {
    */
   function testValidateLimitErrors() {
     $edit = array(
-      'test' => 'invalid', 
+      'test' => 'invalid',
       'test_numeric_index[0]' => 'invalid',
       'test_substring[foo]' => 'invalid',
     );
@@ -820,11 +819,10 @@ class FormsElementsTableSelectFunctionalTest extends DrupalWebTestCase {
 
     drupal_process_form($form_id, $form, $form_state);
 
-    $errors = form_get_errors();
+    $errors = form_get_errors($form_state);
 
     // Clear errors and messages.
     drupal_get_messages();
-    form_clear_error();
 
     // Return the processed form together with form_state and errors
     // to allow the caller lowlevel access to the form.
@@ -1250,7 +1248,7 @@ class FormsProgrammaticTestCase extends DrupalWebTestCase {
     drupal_form_submit('form_test_programmatic_form', $form_state);
 
     // Check that the form returns an error when expected, and vice versa.
-    $errors = form_get_errors();
+    $errors = form_get_errors($form_state);
     $valid_form = empty($errors);
     $args = array(
       '%values' => print_r($values, TRUE),
diff --git a/modules/simpletest/tests/form_test.module b/modules/simpletest/tests/form_test.module
index 00be7d2..605f107 100644
--- a/modules/simpletest/tests/form_test.module
+++ b/modules/simpletest/tests/form_test.module
@@ -281,7 +281,7 @@ function form_test_validate_form($form, &$form_state) {
 /**
  * Form element validation handler for 'name' in form_test_validate_form().
  */
-function form_test_element_validate_name(&$element, &$form_state) {
+function form_test_element_validate_name(&$element, &$form_state, $complete_form) {
   $triggered = FALSE;
   if ($form_state['values']['name'] == 'element_validate') {
     // Alter the form element.
@@ -310,7 +310,7 @@ function form_test_element_validate_name(&$element, &$form_state) {
     drupal_set_message(t('@label value: @value', array('@label' => $element['#title'], '@value' => $form_state['values']['name'])));
 
     // Trigger a form validation error to see our changes.
-    form_set_error('');
+    form_error($complete_form, $form_state);
   }
 }
 
@@ -327,7 +327,7 @@ function form_test_validate_form_validate(&$form, &$form_state) {
     drupal_set_message(t('@label value: @value', array('@label' => $form['name']['#title'], '@value' => $form_state['values']['name'])));
 
     // Trigger a form validation error to see our changes.
-    form_set_error('');
+    form_error($form, $form_state);
   }
 }
 
@@ -399,7 +399,7 @@ function form_test_limit_validation_errors_form($form, &$form_state) {
  */
 function form_test_limit_validation_errors_element_validate_test(&$element, &$form_state) {
   if ($element['#value'] == 'invalid') {
-    form_error($element, t('@label element is invalid', array('@label' => $element['#title'])));
+    form_error($element, $form_state, t('@label element is invalid', array('@label' => $element['#title'])));
   }
 }
 
@@ -1434,7 +1434,7 @@ function form_test_programmatic_form($form, &$form_state) {
  */
 function form_test_programmatic_form_validate($form, &$form_state) {
   if (empty($form_state['values']['textfield'])) {
-    form_set_error('textfield', t('Textfield is required.'));
+    form_error($form['textfield'], $form_state, t('Textfield is required.'));
   }
 }
 
diff --git a/modules/system/image.gd.inc b/modules/system/image.gd.inc
index a3f76d4..fbf5fac 100644
--- a/modules/system/image.gd.inc
+++ b/modules/system/image.gd.inc
@@ -45,7 +45,7 @@ function image_gd_settings_validate($form, &$form_state) {
   // Validate image quality range.
   $value = $form_state['values']['image_jpeg_quality'];
   if (!is_numeric($value) || $value < 0 || $value > 100) {
-    form_set_error('image_jpeg_quality', t('JPEG quality must be a number between 0 and 100.'));
+    form_error($form['image_jpeg_quality'], $form_state, t('JPEG quality must be a number between 0 and 100.'));
   }
 }
 
diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc
index 9e7d69d..4aac027 100644
--- a/modules/system/system.admin.inc
+++ b/modules/system/system.admin.inc
@@ -606,7 +606,7 @@ function system_theme_settings_validate($form, &$form_state) {
     }
     else {
       // File upload failed.
-      form_set_error('logo_upload', t('The logo could not be uploaded.'));
+      form_error($form['logo_upload'], $form_state, t('The logo could not be uploaded.'));
     }
   }
 
@@ -622,7 +622,7 @@ function system_theme_settings_validate($form, &$form_state) {
     }
     else {
       // File upload failed.
-      form_set_error('logo_upload', t('The favicon could not be uploaded.'));
+      form_error($form['logo_upload'], $form_state, t('The favicon could not be uploaded.'));
     }
   }
 
@@ -631,13 +631,13 @@ function system_theme_settings_validate($form, &$form_state) {
   if ($form_state['values']['logo_path']) {
     $path = _system_theme_settings_validate_path($form_state['values']['logo_path']);
     if (!$path) {
-      form_set_error('logo_path', t('The custom logo path is invalid.'));
+      form_error($form['logo_path'], $form_state, t('The custom logo path is invalid.'));
     }
   }
   if ($form_state['values']['favicon_path']) {
     $path = _system_theme_settings_validate_path($form_state['values']['favicon_path']);
     if (!$path) {
-      form_set_error('favicon_path', t('The custom favicon path is invalid.'));
+      form_error($form['favicon_path'], $form_state, t('The custom favicon path is invalid.'));
     }
   }
 }
@@ -1395,13 +1395,13 @@ function system_ip_blocking_form($form, $form_state, $default_ip) {
 function system_ip_blocking_form_validate($form, &$form_state) {
   $ip = trim($form_state['values']['ip']);
   if (db_query("SELECT * FROM {blocked_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField()) {
-    form_set_error('ip', t('This IP address is already blocked.'));
+    form_error($form['ip'], $form_state, t('This IP address is already blocked.'));
   }
   elseif ($ip == ip_address()) {
-    form_set_error('ip', t('You may not block your own IP address.'));
+    form_error($form['ip'], $form_state, t('You may not block your own IP address.'));
   }
   elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) == FALSE) {
-    form_set_error('ip', t('Enter a valid IP address.'));
+    form_error($form['ip'], $form_state, t('Enter a valid IP address.'));
   }
 }
 
@@ -1521,7 +1521,7 @@ function system_site_information_settings() {
 function system_site_information_settings_validate($form, &$form_state) {
   // Validate the e-mail address.
   if ($error = user_validate_mail($form_state['values']['site_mail'])) {
-    form_set_error('site_mail', $error);
+    form_error($form['site_mail'], $form_state, $error);
   }
   // Check for empty front page path.
   if (empty($form_state['values']['site_frontpage'])) {
@@ -1534,7 +1534,7 @@ function system_site_information_settings_validate($form, &$form_state) {
   }
   // Validate front page path.
   if (!drupal_valid_path($form_state['values']['site_frontpage'])) {
-    form_set_error('site_frontpage', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_frontpage'])));
+    form_error($form['site_frontpage'], $form_state, t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_frontpage'])));
   }
   // Get the normal paths of both error pages.
   if (!empty($form_state['values']['site_403'])) {
@@ -1545,11 +1545,11 @@ function system_site_information_settings_validate($form, &$form_state) {
   }
   // Validate 403 error path.
   if (!empty($form_state['values']['site_403']) && !drupal_valid_path($form_state['values']['site_403'])) {
-    form_set_error('site_403', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_403'])));
+    form_error($form['site_403'], $form_state, t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_403'])));
   }
   // Validate 404 error path.
   if (!empty($form_state['values']['site_404']) && !drupal_valid_path($form_state['values']['site_404'])) {
-    form_set_error('site_404', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_404'])));
+    form_error($form['site_404'], $form_state, t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_404'])));
   }
 }
 
@@ -2116,11 +2116,11 @@ function system_add_date_format_type_form($form, &$form_state) {
 function system_add_date_format_type_form_validate($form, &$form_state) {
   if (!empty($form_state['values']['machine_name']) && !empty($form_state['values']['date_type'])) {
     if (!preg_match("/^[a-zA-Z0-9_]+$/", trim($form_state['values']['machine_name']))) {
-      form_set_error('machine_name', t('The date type must contain only alphanumeric characters and underscores.'));
+      form_error($form['machine_name'], $form_state, t('The date type must contain only alphanumeric characters and underscores.'));
     }
     $types = system_get_date_types();
     if (in_array(trim($form_state['values']['machine_name']), array_keys($types))) {
-      form_set_error('machine_name', t('This date type already exists. Enter a unique type.'));
+      form_error($form['machine_name'], $form_state, t('This date type already exists. Enter a unique type.'));
     }
   }
 }
@@ -2834,7 +2834,7 @@ function system_add_date_formats_form_validate($form, &$form_state) {
   $formats = system_get_date_formats('custom');
   $format = trim($form_state['values']['date_format']);
   if (!empty($formats) && in_array($format, array_keys($formats)) && (!isset($form_state['values']['dfid']) || $form_state['values']['dfid'] != $formats[$format]['dfid'])) {
-    form_set_error('date_format', t('This format already exists. Enter a unique format string.'));
+    form_error($form['date_format'], $form_state, t('This format already exists. Enter a unique format string.'));
   }
 }
 
diff --git a/modules/system/system.module b/modules/system/system.module
index 3ebc657..0c162a3 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -2136,7 +2136,7 @@ function system_admin_menu_block($item) {
  * @param $form_element
  *   The form element containing the name of the directory to check.
  */
-function system_check_directory($form_element) {
+function system_check_directory($form_element, &$form_state) {
   $directory = $form_element['#value'];
   if (strlen($directory) == 0) {
     return $form_element;
@@ -2144,13 +2144,13 @@ function system_check_directory($form_element) {
 
   if (!is_dir($directory) && !drupal_mkdir($directory, NULL, TRUE)) {
     // If the directory does not exists and cannot be created.
-    form_set_error($form_element['#parents'][0], t('The directory %directory does not exist and could not be created.', array('%directory' => $directory)));
+    form_error($form_element, $form_state, t('The directory %directory does not exist and could not be created.', array('%directory' => $directory)));
     watchdog('file system', 'The directory %directory does not exist and could not be created.', array('%directory' => $directory), LOG_ERR);
   }
 
   if (is_dir($directory) && !is_writable($directory) && !drupal_chmod($directory)) {
     // If the directory is not writable and cannot be made so.
-    form_set_error($form_element['#parents'][0], t('The directory %directory exists but is not writable and could not be made writable.', array('%directory' => $directory)));
+    form_error($form_element, $form_state, t('The directory %directory exists but is not writable and could not be made writable.', array('%directory' => $directory)));
     watchdog('file system', 'The directory %directory exists but is not writable and could not be made writable.', array('%directory' => $directory), LOG_ERR);
   }
   elseif (is_dir($directory)) {
@@ -2729,9 +2729,10 @@ function system_settings_form($form) {
   $form['actions']['#type'] = 'actions';
   $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
 
+  /* @todo remove this?
   if (!empty($_POST) && form_get_errors()) {
     drupal_set_message(t('The settings have not been saved because of the errors.'), 'error');
-  }
+  }*/
   $form['#submit'][] = 'system_settings_form_submit';
   // By default, render the form using theme_system_settings_form().
   if (!isset($form['#theme'])) {
@@ -3115,12 +3116,12 @@ function system_send_email_action_form($context) {
 /**
  * Validate system_send_email_action form submissions.
  */
-function system_send_email_action_validate($form, $form_state) {
+function system_send_email_action_validate($form, &$form_state) {
   $form_values = $form_state['values'];
   // Validate the configuration form.
   if (!valid_email_address($form_values['recipient']) && strpos($form_values['recipient'], ':mail') === FALSE) {
     // We want the literal %author placeholder to be emphasized in the error message.
-    form_set_error('recipient', t('Enter a valid email address or use a token e-mail address such as %author.', array('%author' => '[node:author:mail]')));
+    form_error($form['recipient'], $form_state, t('Enter a valid email address or use a token e-mail address such as %author.', array('%author' => '[node:author:mail]')));
   }
 }
 
diff --git a/modules/taxonomy/taxonomy.admin.inc b/modules/taxonomy/taxonomy.admin.inc
index 74a362d..726ef7c 100644
--- a/modules/taxonomy/taxonomy.admin.inc
+++ b/modules/taxonomy/taxonomy.admin.inc
@@ -197,7 +197,7 @@ function taxonomy_form_vocabulary_validate($form, &$form_state) {
     $machine_name = $form_state['values']['machine_name'];
     $disallowed = array('add', 'list');
     if (in_array($machine_name, $disallowed)) {
-      form_set_error('machine_name', t('The machine-readable name cannot be "add" or "list".'));
+      form_error($form['machine_name'], $form_state, t('The machine-readable name cannot be "add" or "list".'));
     }
   }
 }
@@ -791,7 +791,7 @@ function taxonomy_form_term_validate($form, &$form_state) {
 
   // Ensure numeric values.
   if (isset($form_state['values']['weight']) && !is_numeric($form_state['values']['weight'])) {
-    form_set_error('weight', t('Weight value must be numeric.'));
+    form_error($form['weight'], $form_state, t('Weight value must be numeric.'));
   }
 }
 
diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module
index dc2847d..446d795 100644
--- a/modules/taxonomy/taxonomy.module
+++ b/modules/taxonomy/taxonomy.module
@@ -1568,7 +1568,7 @@ function taxonomy_autocomplete_validate($element, &$form_state) {
  * Implements hook_field_widget_error().
  */
 function taxonomy_field_widget_error($element, $error, $form, &$form_state) {
-  form_error($element, $error['message']);
+  form_error($element, $form_state, $error['message']);
 }
 /**
  * Implements hook_field_settings_form().
diff --git a/modules/translation/translation.module b/modules/translation/translation.module
index 697929f..e538fb3 100644
--- a/modules/translation/translation.module
+++ b/modules/translation/translation.module
@@ -375,13 +375,13 @@ function translation_node_update($node) {
  *
  * Ensure that duplicate translations can not be created for the same source.
  */
-function translation_node_validate($node, $form) {
+function translation_node_validate($node, $form, &$form_state) {
   // Only act on translatable nodes with a tnid or translation_source.
   if (translation_supported_type($node->type) && (!empty($node->tnid) || !empty($form['#node']->translation_source->nid))) {
     $tnid = !empty($node->tnid) ? $node->tnid : $form['#node']->translation_source->nid;
     $translations = translation_node_get_translations($tnid);
     if (isset($translations[$node->language]) && $translations[$node->language]->nid != $node->nid ) {
-      form_set_error('language', t('There is already a translation in this language.'));
+      form_error($form['language'], $form_state, t('There is already a translation in this language.'));
     }
   }
 }
diff --git a/modules/trigger/trigger.admin.inc b/modules/trigger/trigger.admin.inc
index 7509eb3..5f5615f 100644
--- a/modules/trigger/trigger.admin.inc
+++ b/modules/trigger/trigger.admin.inc
@@ -201,7 +201,7 @@ function trigger_assign_form($form, $form_state, $module, $hook, $label) {
  *
  * Makes sure that the user is not re-assigning an action to an event.
  */
-function trigger_assign_form_validate($form, $form_state) {
+function trigger_assign_form_validate($form, &$form_state) {
   $form_values = $form_state['values'];
   if (!empty($form_values['aid'])) {
     $aid = actions_function_lookup($form_values['aid']);
@@ -210,7 +210,7 @@ function trigger_assign_form_validate($form, $form_state) {
       ':aid' => $aid,
     ))->fetchField();
     if ($aid_exists) {
-      form_set_error($form_values['hook'], t('The action you chose is already assigned to that trigger.'));
+      form_error($form[$form_values['hook']], $form_state, t('The action you chose is already assigned to that trigger.'));
     }
   }
 }
diff --git a/modules/update/update.manager.inc b/modules/update/update.manager.inc
index 35b2929..a960864 100644
--- a/modules/update/update.manager.inc
+++ b/modules/update/update.manager.inc
@@ -284,7 +284,7 @@ function update_manager_update_form_validate($form, &$form_state) {
     $disabled = array_filter($form_state['values']['disabled_projects']);
   }
   if (empty($enabled) && empty($disabled)) {
-    form_set_error('projects', t('You must select at least one project to update.'));
+    form_error($form['projects'], $form_state, t('You must select at least one project to update.'));
   }
 }
 
@@ -584,12 +584,12 @@ function _update_manager_check_backends(&$form, $operation) {
  */
 function update_manager_install_form_validate($form, &$form_state) {
   if (!($form_state['values']['project_url'] XOR !empty($_FILES['files']['name']['project_upload']))) {
-    form_set_error('project_url', t('You must either provide a URL or upload an archive file to install.'));
+    form_error($form['project_url'], $form_state, t('You must either provide a URL or upload an archive file to install.'));
   }
 
   if ($form_state['values']['project_url']) {
     if (!valid_url($form_state['values']['project_url'], TRUE)) {
-      form_set_error('project_url', t('The provided URL is invalid.'));
+      form_error($form['project_url'], $form_state, t('The provided URL is invalid.'));
     }
   }
 }
@@ -614,7 +614,7 @@ function update_manager_install_form_submit($form, &$form_state) {
     $field = 'project_url';
     $local_cache = update_manager_file_get($form_state['values']['project_url']);
     if (!$local_cache) {
-      form_set_error($field, t('Unable to retrieve Drupal project from %url.', array('%url' => $form_state['values']['project_url'])));
+      form_error($form[$field], $form_state, t('Unable to retrieve Drupal project from %url.', array('%url' => $form_state['values']['project_url'])));
       return;
     }
   }
@@ -634,13 +634,13 @@ function update_manager_install_form_submit($form, &$form_state) {
     $archive = update_manager_archive_extract($local_cache, $directory);
   }
   catch (Exception $e) {
-    form_set_error($field, $e->getMessage());
+    form_error($form[$field], $form_state, $e->getMessage());
     return;
   }
 
   $files = $archive->listContents();
   if (!$files) {
-    form_set_error($field, t('Provided archive contains no files.'));
+    form_error($form[$field], $form_state, t('Provided archive contains no files.'));
     return;
   }
 
@@ -651,7 +651,7 @@ function update_manager_install_form_submit($form, &$form_state) {
 
   $archive_errors = update_manager_archive_verify($project, $local_cache, $directory);
   if (!empty($archive_errors)) {
-    form_set_error($field, array_shift($archive_errors));
+    form_error($form[$field], $form_state, array_shift($archive_errors));
     // @todo: Fix me in D8: We need a way to set multiple errors on the same
     // form element and have all of them appear!
     if (!empty($archive_errors)) {
@@ -670,7 +670,7 @@ function update_manager_install_form_submit($form, &$form_state) {
     $updater = Updater::factory($project_location);
   }
   catch (Exception $e) {
-    form_set_error($field, $e->getMessage());
+    form_error($form[$field], $form_state, $e->getMessage());
     return;
   }
 
@@ -678,16 +678,16 @@ function update_manager_install_form_submit($form, &$form_state) {
     $project_title = Updater::getProjectTitle($project_location);
   }
   catch (Exception $e) {
-    form_set_error($field, $e->getMessage());
+    form_error($form[$field], $form_state, $e->getMessage());
     return;
   }
 
   if (!$project_title) {
-    form_set_error($field, t('Unable to determine %project name.', array('%project' => $project)));
+    form_error($form[$field], $form_state, t('Unable to determine %project name.', array('%project' => $project)));
   }
 
   if ($updater->isInstalled()) {
-    form_set_error($field, t('%project is already installed.', array('%project' => $project_title)));
+    form_error($form[$field], $form_state, t('%project is already installed.', array('%project' => $project_title)));
     return;
   }
 
diff --git a/modules/update/update.settings.inc b/modules/update/update.settings.inc
index 60ac3ca..2459f3d 100644
--- a/modules/update/update.settings.inc
+++ b/modules/update/update.settings.inc
@@ -80,10 +80,10 @@ function update_settings_validate($form, &$form_state) {
       $form_state['notify_emails'] = $valid;
     }
     elseif (count($invalid) == 1) {
-      form_set_error('update_notify_emails', t('%email is not a valid e-mail address.', array('%email' => reset($invalid))));
+      form_error($form['update_notify_emails'], $form_state, t('%email is not a valid e-mail address.', array('%email' => reset($invalid))));
     }
     else {
-      form_set_error('update_notify_emails', t('%emails are not valid e-mail addresses.', array('%emails' => implode(', ', $invalid))));
+      form_error($form['update_notify_emails'], $form_state, t('%emails are not valid e-mail addresses.', array('%emails' => implode(', ', $invalid))));
     }
   }
 }
diff --git a/modules/user/user.admin.inc b/modules/user/user.admin.inc
index afaddeb..291e716 100644
--- a/modules/user/user.admin.inc
+++ b/modules/user/user.admin.inc
@@ -252,7 +252,7 @@ function user_admin_account_submit($form, &$form_state) {
 function user_admin_account_validate($form, &$form_state) {
   $form_state['values']['accounts'] = array_filter($form_state['values']['accounts']);
   if (count($form_state['values']['accounts']) == 0) {
-    form_set_error('', t('No users selected.'));
+    form_error($form['accounts'], $form_state, t('No users selected.'));
   }
 }
 
@@ -262,7 +262,7 @@ function user_admin_account_validate($form, &$form_state) {
  * @ingroup forms
  * @see system_settings_form()
  */
-function user_admin_settings() {
+function user_admin_settings($form, &$form_state) {
   // Settings for anonymous users.
   $form['anonymous_settings'] = array(
     '#type' => 'fieldset',
@@ -351,7 +351,7 @@ function user_admin_settings() {
   if (variable_get('user_pictures', 0)) {
     $picture_path =  file_default_scheme() . '://' . variable_get('user_picture_path', 'pictures');
     if (!file_prepare_directory($picture_path, FILE_CREATE_DIRECTORY)) {
-      form_set_error('user_picture_path', t('The directory %directory does not exist or is not writable.', array('%directory' => $picture_path)));
+      form_error($form['user_picture_path'], $form_state, t('The directory %directory does not exist or is not writable.', array('%directory' => $picture_path)));
       watchdog('file system', 'The directory %directory does not exist or is not writable.', array('%directory' => $picture_path), LOG_ERR);
     }
   }
@@ -972,17 +972,17 @@ function user_admin_role_validate($form, &$form_state) {
     if ($form_state['values']['op'] == t('Save role')) {
       $role = user_role_load_by_name($form_state['values']['name']);
       if ($role && $role->rid != $form_state['values']['rid']) {
-        form_set_error('name', t('The role name %name already exists. Choose another role name.', array('%name' => $form_state['values']['name'])));
+        form_error($form['name'], $form_state, t('The role name %name already exists. Choose another role name.', array('%name' => $form_state['values']['name'])));
       }
     }
     elseif ($form_state['values']['op'] == t('Add role')) {
       if (user_role_load_by_name($form_state['values']['name'])) {
-        form_set_error('name', t('The role name %name already exists. Choose another role name.', array('%name' => $form_state['values']['name'])));
+        form_error($form['name'], $form_state, t('The role name %name already exists. Choose another role name.', array('%name' => $form_state['values']['name'])));
       }
     }
   }
   else {
-    form_set_error('name', t('You must specify a valid role name.'));
+    form_error($form['name'], $form_state, t('You must specify a valid role name.'));
   }
 }
 
diff --git a/modules/user/user.module b/modules/user/user.module
index ffd170c..0b0b96c 100644
--- a/modules/user/user.module
+++ b/modules/user/user.module
@@ -668,7 +668,7 @@ function user_validate_picture(&$form, &$form_state) {
   // Save the file as a temporary file.
   $file = file_save_upload('picture_upload', $validators);
   if ($file === FALSE) {
-    form_set_error('picture_upload', t("Failed to upload the picture image; the %directory directory doesn't exist or is not writable.", array('%directory' => variable_get('user_picture_path', 'pictures'))));
+    form_error($form['picture_upload'], $form_state, t("Failed to upload the picture image; the %directory directory doesn't exist or is not writable.", array('%directory' => variable_get('user_picture_path', 'pictures'))));
   }
   elseif ($file !== NULL) {
     $form_state['values']['picture_upload'] = $file;
@@ -1164,8 +1164,8 @@ function user_validate_current_pass(&$form, &$form_state) {
       require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
       $current_pass_failed = empty($form_state['values']['current_pass']) || !user_check_password($form_state['values']['current_pass'], $account);
       if ($current_pass_failed) {
-        form_set_error('current_pass', t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => $name)));
-        form_set_error($key);
+        form_error($form['current_pass'], $form_state, t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => $name)));
+        form_error($form[$key], $form_state);
       }
       // We only need to check the password once.
       break;
@@ -1184,10 +1184,10 @@ function user_account_form_validate($form, &$form_state) {
     // Validate new or changing username.
     if (isset($form_state['values']['name'])) {
       if ($error = user_validate_name($form_state['values']['name'])) {
-        form_set_error('name', $error);
+        form_error($form['name'], $form_state, $error);
       }
       elseif ((bool) db_select('users')->fields('users', array('uid'))->condition('uid', $account->uid, '<>')->condition('name', db_like($form_state['values']['name']), 'LIKE')->range(0, 1)->execute()->fetchField()) {
-        form_set_error('name', t('The name %name is already taken.', array('%name' => $form_state['values']['name'])));
+        form_error($form['name'], $form_state, t('The name %name is already taken.', array('%name' => $form_state['values']['name'])));
       }
     }
 
@@ -1198,15 +1198,15 @@ function user_account_form_validate($form, &$form_state) {
 
     // Validate the e-mail address, and check if it is taken by an existing user.
     if ($error = user_validate_mail($form_state['values']['mail'])) {
-      form_set_error('mail', $error);
+      form_error($form['mail'], $form_state, $error);
     }
     elseif ((bool) db_select('users')->fields('users', array('uid'))->condition('uid', $account->uid, '<>')->condition('mail', db_like($form_state['values']['mail']), 'LIKE')->range(0, 1)->execute()->fetchField()) {
       // Format error message dependent on whether the user is logged in or not.
       if ($GLOBALS['user']->uid) {
-        form_set_error('mail', t('The e-mail address %email is already taken.', array('%email' => $form_state['values']['mail'])));
+        form_error($form['mail'], $form_state, t('The e-mail address %email is already taken.', array('%email' => $form_state['values']['mail'])));
       }
       else {
-        form_set_error('mail', t('The e-mail address %email is already registered. <a href="@password">Have you forgotten your password?</a>', array('%email' => $form_state['values']['mail'], '@password' => url('user/password'))));
+        form_error($form['mail'], $form_state, t('The e-mail address %email is already registered. <a href="@password">Have you forgotten your password?</a>', array('%email' => $form_state['values']['mail'], '@password' => url('user/password'))));
       }
     }
 
@@ -1220,7 +1220,7 @@ function user_account_form_validate($form, &$form_state) {
 
       $user_schema = drupal_get_schema('users');
       if (drupal_strlen($form_state['values']['signature']) > $user_schema['fields']['signature']['length']) {
-        form_set_error('signature', t('The signature is too long: it must be %max characters or less.', array('%max' => $user_schema['fields']['signature']['length'])));
+        form_error($form['signature'], $form_state, t('The signature is too long: it must be %max characters or less.', array('%max' => $user_schema['fields']['signature']['length'])));
       }
     }
   }
@@ -2065,7 +2065,7 @@ function user_login_default_validators() {
 function user_login_name_validate($form, &$form_state) {
   if (isset($form_state['values']['name']) && user_is_blocked($form_state['values']['name'])) {
     // Blocked in user administration.
-    form_set_error('name', t('The username %name has not been activated or is blocked.', array('%name' => $form_state['values']['name'])));
+    form_error($form['name'], $form_state, t('The username %name has not been activated or is blocked.', array('%name' => $form_state['values']['name'])));
   }
 }
 
@@ -2132,15 +2132,15 @@ function user_login_final_validate($form, &$form_state) {
 
     if (isset($form_state['flood_control_triggered'])) {
       if ($form_state['flood_control_triggered'] == 'user') {
-        form_set_error('name', format_plural(variable_get('user_failed_login_user_limit', 5), 'Sorry, there has been more than one failed login attempt for this account. It is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', 'Sorry, there have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', array('@url' => url('user/password'))));
+        form_error($form['name'], $form_state, format_plural(variable_get('user_failed_login_user_limit', 5), 'Sorry, there has been more than one failed login attempt for this account. It is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', 'Sorry, there have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', array('@url' => url('user/password'))));
       }
       else {
         // We did not find a uid, so the limit is IP-based.
-        form_set_error('name', t('Sorry, too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', array('@url' => url('user/password'))));
+        form_error($form['name'], $form_state, t('Sorry, too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', array('@url' => url('user/password'))));
       }
     }
     else {
-      form_set_error('name', t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>', array('@password' => url('user/password'))));
+      form_error($form['name'], $form_state, t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>', array('@password' => url('user/password'))));
       watchdog('user', 'Login attempt failed for %user.', array('%user' => $form_state['values']['name']));
     }
   }
diff --git a/modules/user/user.pages.inc b/modules/user/user.pages.inc
index 0d06ff2..b9ce8a8 100644
--- a/modules/user/user.pages.inc
+++ b/modules/user/user.pages.inc
@@ -67,7 +67,7 @@ function user_pass_validate($form, &$form_state) {
     form_set_value(array('#parents' => array('account')), $account, $form_state);
   }
   else {
-    form_set_error('name', t('Sorry, %name is not recognized as a user name or an e-mail address.', array('%name' => $name)));
+    form_error($form['name'], $form_state, t('Sorry, %name is not recognized as a user name or an e-mail address.', array('%name' => $name)));
   }
 }
 
