diff --git a/core/core.services.yml b/core/core.services.yml
index e791572..0a052ff 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -331,13 +331,10 @@ services:
     arguments: ['@form_validator', '@form_submitter', '@form_cache', '@module_handler', '@event_dispatcher', '@request_stack', '@class_resolver', '@element_info', '@theme.manager', '@?csrf_token']
   form_validator:
     class: Drupal\Core\Form\FormValidator
-    arguments: ['@request_stack', '@string_translation', '@csrf_token', '@logger.channel.form', '@form_error_handler']
+    arguments: ['@request_stack', '@string_translation', '@csrf_token', '@logger.channel.form']
   form_submitter:
     class: Drupal\Core\Form\FormSubmitter
     arguments: ['@request_stack', '@url_generator']
-  form_error_handler:
-    class: Drupal\Core\Form\FormErrorHandler
-    arguments: ['@string_translation', '@link_generator', '@renderer']
   form_cache:
     class: Drupal\Core\Form\FormCache
     arguments: ['@app.root', '@keyvalue.expirable', '@module_handler', '@current_user', '@csrf_token', '@logger.channel.form', '@request_stack', '@page_cache_request_policy']
diff --git a/core/includes/form.inc b/core/includes/form.inc
index 16353d8..6cc7027 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -11,7 +11,6 @@
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Form\FormElementHelper;
 use Drupal\Core\Form\OptGroup;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Template\Attribute;
@@ -222,12 +221,6 @@ function template_preprocess_fieldset(&$variables) {
     // Add the description's id to the fieldset aria attributes.
     $variables['attributes']['aria-describedby'] = $description_id;
   }
-
-  // Display any error messages.
-  $variables['errors'] = NULL;
-  if (!empty($element['#errors']) && empty($element['#error_no_message'])) {
-    $variables['errors'] = $element['#errors'];
-  }
 }
 
 /**
@@ -257,12 +250,6 @@ function template_preprocess_details(&$variables) {
   $variables['description'] = (!empty($element['#description'])) ? $element['#description'] : '';
   $variables['children'] = (isset($element['#children'])) ? $element['#children'] : '';
   $variables['value'] = (isset($element['#value'])) ? $element['#value'] : '';
-
-  // Display any error messages.
-  $variables['errors'] = NULL;
-  if (!empty($element['#errors']) && empty($element['#error_no_message'])) {
-    $variables['errors'] = $element['#errors'];
-  }
 }
 
 /**
@@ -438,7 +425,7 @@ function template_preprocess_form_element(&$variables) {
   );
   $variables['attributes'] = $element['#wrapper_attributes'];
 
-  // Add element #id for #type 'item' and 'password_confirm'.
+  // Add element #id for #type 'item'.
   if (isset($element['#markup']) && !empty($element['#id'])) {
     $variables['attributes']['id'] = $element['#id'];
   }
@@ -454,12 +441,6 @@ function template_preprocess_form_element(&$variables) {
   // Pass elements disabled status to template.
   $variables['disabled'] = !empty($element['#attributes']['disabled']) ? $element['#attributes']['disabled'] : NULL;
 
-  // Display any error messages.
-  $variables['errors'] = NULL;
-  if (!empty($element['#errors']) && empty($element['#error_no_message'])) {
-    $variables['errors'] = $element['#errors'];
-  }
-
   // If #title is not set, we don't display any label.
   if (!isset($element['#title'])) {
     $element['#title_display'] = 'none';
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 61bc83f..bdca76c 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -546,12 +546,6 @@ function template_preprocess_datetime_wrapper(&$variables) {
     $variables['title'] = $element['#title'];
   }
 
-  // Display any error messages.
-  $variables['errors'] = NULL;
-  if (!empty($element['#errors']) && empty($element['#error_no_message'])) {
-    $variables['errors'] = $element['#errors'];
-  }
-
   if (!empty($element['#description'])) {
     $variables['description'] = $element['#description'];
   }
diff --git a/core/lib/Drupal/Core/Datetime/Element/Datelist.php b/core/lib/Drupal/Core/Datetime/Element/Datelist.php
index 2caa4ca..9771a4f 100644
--- a/core/lib/Drupal/Core/Datetime/Element/Datelist.php
+++ b/core/lib/Drupal/Core/Datetime/Element/Datelist.php
@@ -267,7 +267,6 @@ public static function processDatelist(&$element, FormStateInterface $form_state
         '#attributes' => $element['#attributes'],
         '#options' => $options,
         '#required' => $element['#required'],
-        '#error_no_message' => FALSE,
       );
     }
 
diff --git a/core/lib/Drupal/Core/Datetime/Element/Datetime.php b/core/lib/Drupal/Core/Datetime/Element/Datetime.php
index 3eb7c84..9d14f7b 100644
--- a/core/lib/Drupal/Core/Datetime/Element/Datetime.php
+++ b/core/lib/Drupal/Core/Datetime/Element/Datetime.php
@@ -267,7 +267,6 @@ public static function processDatetime(&$element, FormStateInterface $form_state
         '#attributes' => $element['#attributes'] + $extra_attributes,
         '#required' => $element['#required'],
         '#size' => max(12, strlen($element['#value']['date'])),
-        '#error_no_message' => TRUE,
         '#date_date_format' => $element['#date_date_format'],
       );
 
@@ -300,7 +299,6 @@ public static function processDatetime(&$element, FormStateInterface $form_state
         '#attributes' => $element['#attributes'] + $extra_attributes,
         '#required' => $element['#required'],
         '#size' => 12,
-        '#error_no_message' => TRUE,
       );
 
       // Allows custom callbacks to alter the element.
diff --git a/core/lib/Drupal/Core/Form/FormElementHelper.php b/core/lib/Drupal/Core/Form/FormElementHelper.php
deleted file mode 100644
index 013eaa1..0000000
--- a/core/lib/Drupal/Core/Form/FormElementHelper.php
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Form\FormElementHelper.
- */
-
-namespace Drupal\Core\Form;
-
-use Drupal\Core\Render\Element;
-
-/**
- * Provides common functionality for form elements.
- */
-class FormElementHelper {
-
-  /**
-   * Retrieves a form element.
-   *
-   * @param string $name
-   *   The name of the form element. If the #parents property of your form
-   *   element is ['foo', 'bar', 'baz'] then the name is 'foo][bar][baz'.
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   *
-   * @return array
-   *   The form element.
-   */
-  public static function getElementByName($name, array $form) {
-    foreach (Element::children($form) as $key) {
-      if (implode('][', $form[$key]['#parents']) === $name) {
-        return $form[$key];
-      }
-      elseif ($element = static::getElementByName($name, $form[$key])) {
-        return $element;
-      }
-    }
-    return [];
-  }
-
-  /**
-   * Returns the title for the element.
-   *
-   * If the element has no title, this will recurse through all children of the
-   * element until a title is found.
-   *
-   * @param array $element
-   *   An associative array containing the properties of the form element.
-   *
-   * @return string
-   *   The title of the element, or an empty string if none is found.
-   */
-  public static function getElementTitle(array $element) {
-    $title = '';
-    if (isset($element['#title'])) {
-      $title = $element['#title'];
-    }
-    else {
-      foreach (Element::children($element) as $key) {
-        if ($title = static::getElementTitle($element[$key])) {
-          break;
-        }
-      }
-    }
-    return $title;
-  }
-
-}
diff --git a/core/lib/Drupal/Core/Form/FormErrorHandler.php b/core/lib/Drupal/Core/Form/FormErrorHandler.php
deleted file mode 100644
index 870fbf0..0000000
--- a/core/lib/Drupal/Core/Form/FormErrorHandler.php
+++ /dev/null
@@ -1,153 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Form\FormErrorHandler.
- */
-
-namespace Drupal\Core\Form;
-
-use Drupal\Core\Render\Element;
-use Drupal\Core\Routing\LinkGeneratorTrait;
-use Drupal\Core\Render\RendererInterface;
-use Drupal\Core\StringTranslation\StringTranslationTrait;
-use Drupal\Core\StringTranslation\TranslationInterface;
-use Drupal\Core\Url;
-use Drupal\Core\Utility\LinkGeneratorInterface;
-
-/**
- * Handles form errors.
- */
-class FormErrorHandler implements FormErrorHandlerInterface {
-
-  use StringTranslationTrait;
-  use LinkGeneratorTrait;
-
-  /**
-   * The renderer service.
-   *
-   * @var \Drupal\Core\Render\RendererInterface
-   */
-  protected $renderer;
-
-  /**
-   * Constructs a new FormErrorHandler.
-   *
-   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
-   *   The string translation service.
-   * @param \Drupal\Core\Utility\LinkGeneratorInterface $link_generator
-   *   The link generation service.
-   * @param \Drupal\Core\Render\RendererInterface $renderer
-   *   The renderer service.
-   */
-  public function __construct(TranslationInterface $string_translation, LinkGeneratorInterface $link_generator, RendererInterface $renderer) {
-    $this->stringTranslation = $string_translation;
-    $this->linkGenerator = $link_generator;
-    $this->renderer = $renderer;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function handleFormErrors(array &$form, FormStateInterface $form_state) {
-    // After validation check if there are errors.
-    if ($errors = $form_state->getErrors()) {
-      // Display error messages for each element.
-      $this->displayErrorMessages($form, $form_state);
-
-      // Loop through and assign each element its errors.
-      $this->setElementErrorsFromFormState($form, $form_state);
-    }
-
-    return $this;
-  }
-
-  /**
-   * Loops through and displays all form errors.
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   */
-  protected function displayErrorMessages(array $form, FormStateInterface $form_state) {
-    $error_links = [];
-    $errors = $form_state->getErrors();
-    // Loop through all form errors and check if we need to display a link.
-    foreach ($errors as $name => $error) {
-      $form_element = FormElementHelper::getElementByName($name, $form);
-      $title = FormElementHelper::getElementTitle($form_element);
-
-      // Only show links to erroneous elements that are visible.
-      $is_visible_element = Element::isVisibleElement($form_element);
-      // Only show links for elements that have a title themselves or have
-      // children with a title.
-      $has_title = !empty($title);
-      // Only show links for elements with an ID.
-      $has_id = !empty($form_element['#id']);
-
-      // Do not show links to elements with suppressed messages. Most often
-      // their parent element is used for inline errors.
-      if (!empty($form_element['#error_no_message'])) {
-        unset($errors[$name]);
-      }
-      elseif ($is_visible_element && $has_title && $has_id) {
-        $error_links[] = $this->l($title, Url::fromRoute('<none>', [], ['fragment' => $form_element['#id'], 'external' => TRUE]));
-        unset($errors[$name]);
-      }
-    }
-
-    // Set normal error messages for all remaining errors.
-    foreach ($errors as $error) {
-      $this->drupalSetMessage($error, 'error');
-    }
-
-    if (!empty($error_links)) {
-      $render_array = [
-        [
-         '#markup' => $this->formatPlural(count($error_links), '1 error has been found: ', '@count errors have been found: '),
-        ],
-        [
-          '#theme' => 'item_list',
-          '#items' => $error_links,
-          '#context' => ['list_style' => 'comma-list'],
-        ],
-      ];
-      $message = $this->renderer->renderPlain($render_array);
-      $this->drupalSetMessage($message, 'error');
-    }
-  }
-
-  /**
-   * Stores the errors of each element directly on the element.
-   *
-   * We must provide a way for non-form functions to check the errors for a
-   * specific element. The most common usage of this is a #pre_render callback.
-   *
-   * @param array $elements
-   *   An associative array containing the structure of a form element.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   */
-  protected function setElementErrorsFromFormState(array &$elements, FormStateInterface &$form_state) {
-    // Recurse through all children.
-    foreach (Element::children($elements) as $key) {
-      if (isset($elements[$key]) && $elements[$key]) {
-        $this->setElementErrorsFromFormState($elements[$key], $form_state);
-      }
-    }
-
-    // Store the errors for this element on the element directly.
-    $elements['#errors'] = $form_state->getError($elements);
-  }
-
-  /**
-   * Wraps drupal_set_message().
-   *
-   * @codeCoverageIgnore
-   */
-  protected function drupalSetMessage($message = NULL, $type = 'status', $repeat = FALSE) {
-    drupal_set_message($message, $type, $repeat);
-  }
-
-}
diff --git a/core/lib/Drupal/Core/Form/FormErrorHandlerInterface.php b/core/lib/Drupal/Core/Form/FormErrorHandlerInterface.php
deleted file mode 100644
index 161505f..0000000
--- a/core/lib/Drupal/Core/Form/FormErrorHandlerInterface.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Form\FormErrorHandlerInterface.
- */
-
-namespace Drupal\Core\Form;
-
-/**
- * Provides an interface for handling form errors.
- */
-interface FormErrorHandlerInterface {
-
-  /**
-   * Handles form errors after form validation.
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   *
-   * @return $this
-   */
-  public function handleFormErrors(array &$form, FormStateInterface $form_state);
-
-}
diff --git a/core/lib/Drupal/Core/Form/FormState.php b/core/lib/Drupal/Core/Form/FormState.php
index bd47f1e..96968de 100644
--- a/core/lib/Drupal/Core/Form/FormState.php
+++ b/core/lib/Drupal/Core/Form/FormState.php
@@ -1145,6 +1145,9 @@ public function setErrorByName($name, $message = '') {
         $errors[$name] = $message;
         $this->errors = $errors;
         static::setAnyErrors();
+        if ($message) {
+          $this->drupalSetMessage($message, 'error');
+        }
       }
     }
 
@@ -1312,6 +1315,15 @@ public function hasInvalidToken() {
   }
 
   /**
+   * Wraps drupal_set_message().
+   *
+   * @return array|null
+   */
+  protected function drupalSetMessage($message = NULL, $type = 'status', $repeat = FALSE) {
+    return drupal_set_message($message, $type, $repeat);
+  }
+
+  /**
    * Wraps ModuleHandler::loadInclude().
    */
   protected function moduleLoadInclude($module, $type, $name = NULL) {
diff --git a/core/lib/Drupal/Core/Form/FormStateInterface.php b/core/lib/Drupal/Core/Form/FormStateInterface.php
index 11b089b..798d12c 100644
--- a/core/lib/Drupal/Core/Form/FormStateInterface.php
+++ b/core/lib/Drupal/Core/Form/FormStateInterface.php
@@ -417,8 +417,7 @@ public static function hasAnyErrors();
    * 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) and the error
-   * message near the element.
+   * rendered with an 'error' CSS class (shown as red by default).
    *
    * The standard behavior of this method can be changed if a button provides
    * the #limit_validation_errors property. Multistep forms not wanting to
diff --git a/core/lib/Drupal/Core/Form/FormValidator.php b/core/lib/Drupal/Core/Form/FormValidator.php
index afead3d..f2f15ee 100644
--- a/core/lib/Drupal/Core/Form/FormValidator.php
+++ b/core/lib/Drupal/Core/Form/FormValidator.php
@@ -45,13 +45,6 @@ class FormValidator implements FormValidatorInterface {
   protected $logger;
 
   /**
-   * The form error handler.
-   *
-   * @var \Drupal\Core\Form\FormErrorHandlerInterface
-   */
-  protected $formErrorHandler;
-
-  /**
    * Constructs a new FormValidator.
    *
    * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
@@ -62,15 +55,12 @@ class FormValidator implements FormValidatorInterface {
    *   The CSRF token generator.
    * @param \Psr\Log\LoggerInterface $logger
    *   A logger instance.
-   * @param \Drupal\Core\Form\FormErrorHandlerInterface $form_error_handler
-   *   The form error handler.
    */
-  public function __construct(RequestStack $request_stack, TranslationInterface $string_translation, CsrfTokenGenerator $csrf_token, LoggerInterface $logger, FormErrorHandlerInterface $form_error_handler) {
+  public function __construct(RequestStack $request_stack, TranslationInterface $string_translation, CsrfTokenGenerator $csrf_token, LoggerInterface $logger) {
     $this->requestStack = $request_stack;
     $this->stringTranslation = $string_translation;
     $this->csrfToken = $csrf_token;
     $this->logger = $logger;
-    $this->formErrorHandler = $form_error_handler;
   }
 
   /**
@@ -203,9 +193,8 @@ protected function handleErrorsWithLimitedValidation(&$form, FormStateInterface
    *   The unique string identifying the form.
    */
   protected function finalizeValidation(&$form, FormStateInterface &$form_state, $form_id) {
-    // Delegate handling of form errors to a service.
-    $this->formErrorHandler->handleFormErrors($form, $form_state);
-
+    // After validation, loop through and assign each element its errors.
+    $this->setElementErrorsFromFormState($form, $form_state);
     // Mark this form as validated.
     $form_state->setValidationComplete();
   }
@@ -414,4 +403,26 @@ protected function determineLimitValidationErrors(FormStateInterface &$form_stat
     }
   }
 
+  /**
+   * Stores the errors of each element directly on the element.
+   *
+   * We must provide a way for non-form functions to check the errors for a
+   * specific element. The most common usage of this is a #pre_render callback.
+   *
+   * @param array $elements
+   *   An associative array containing the structure of a form element.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   */
+  protected function setElementErrorsFromFormState(array &$elements, FormStateInterface &$form_state) {
+    // Recurse through all children.
+    foreach (Element::children($elements) as $key) {
+      if (isset($elements[$key]) && $elements[$key]) {
+        $this->setElementErrorsFromFormState($elements[$key], $form_state);
+      }
+    }
+    // Store the errors for this element on the element directly.
+    $elements['#errors'] = $form_state->getError($elements);
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Render/Element/Checkboxes.php b/core/lib/Drupal/Core/Render/Element/Checkboxes.php
index 117f839..d931460 100644
--- a/core/lib/Drupal/Core/Render/Element/Checkboxes.php
+++ b/core/lib/Drupal/Core/Render/Element/Checkboxes.php
@@ -86,8 +86,6 @@ public static function processCheckboxes(&$element, FormStateInterface $form_sta
           '#default_value' => isset($value[$key]) ? $key : NULL,
           '#attributes' => $element['#attributes'],
           '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
-          // Errors should only be shown on the parent checkboxes element.
-          '#error_no_message' => TRUE,
           '#weight' => $weight,
         );
       }
diff --git a/core/lib/Drupal/Core/Render/Element/PasswordConfirm.php b/core/lib/Drupal/Core/Render/Element/PasswordConfirm.php
index 00295d5..302ed0c 100644
--- a/core/lib/Drupal/Core/Render/Element/PasswordConfirm.php
+++ b/core/lib/Drupal/Core/Render/Element/PasswordConfirm.php
@@ -76,7 +76,6 @@ public static function processPasswordConfirm(&$element, FormStateInterface $for
       '#value' => empty($element['#value']) ? NULL : $element['#value']['pass1'],
       '#required' => $element['#required'],
       '#attributes' => array('class' => array('password-field', 'js-password-field')),
-      '#error_no_message' => TRUE,
     );
     $element['pass2'] =  array(
       '#type' => 'password',
@@ -84,7 +83,6 @@ public static function processPasswordConfirm(&$element, FormStateInterface $for
       '#value' => empty($element['#value']) ? NULL : $element['#value']['pass2'],
       '#required' => $element['#required'],
       '#attributes' => array('class' => array('password-confirm', 'js-password-confirm')),
-      '#error_no_message' => TRUE,
     );
     $element['#element_validate'] = array(array(get_called_class(), 'validatePasswordConfirm'));
     $element['#tree'] = TRUE;
diff --git a/core/lib/Drupal/Core/Render/Element/Radios.php b/core/lib/Drupal/Core/Render/Element/Radios.php
index 5423ef0..619c0b9 100644
--- a/core/lib/Drupal/Core/Render/Element/Radios.php
+++ b/core/lib/Drupal/Core/Render/Element/Radios.php
@@ -83,8 +83,6 @@ public static function processRadios(&$element, FormStateInterface $form_state,
           '#parents' => $element['#parents'],
           '#id' => HtmlUtility::getUniqueId('edit-' . implode('-', $parents_for_id)),
           '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
-          // Errors should only be shown on the parent radios element.
-          '#error_no_message' => TRUE,
           '#weight' => $weight,
         );
       }
diff --git a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
index de8c349..9ed2e46 100644
--- a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
+++ b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
@@ -317,7 +317,7 @@ protected function doTestAuthoringInfo() {
       'content_translation[created]' => '19/11/1978',
     );
     $this->drupalPostForm($entity->urlInfo('edit-form'), $edit, $this->getFormSubmitAction($entity, $langcode));
-    $this->assertTrue($this->xpath('//div[contains(concat(" ", normalize-space(@class), " "), :class)]', array(':class' => ' messages--error ')), 'Invalid values generate a form error message.');
+    $this->assertTrue($this->xpath('//div[contains(@class, "error")]//ul'), 'Invalid values generate a list of form errors.');
     $metadata = $this->manager->getTranslationMetadata($entity->getTranslation($langcode));
     $this->assertEqual($metadata->getAuthor()->id(), $values[$langcode]['uid'], 'Translation author correctly kept.');
     $this->assertEqual($metadata->getCreatedTime(), $values[$langcode]['created'], 'Translation date correctly kept.');
diff --git a/core/modules/file/src/Element/ManagedFile.php b/core/modules/file/src/Element/ManagedFile.php
index 1378ff3..72e44b0 100644
--- a/core/modules/file/src/Element/ManagedFile.php
+++ b/core/modules/file/src/Element/ManagedFile.php
@@ -284,7 +284,6 @@ public static function processManagedFile(&$element, FormStateInterface $form_st
       '#multiple' => $element['#multiple'],
       '#theme_wrappers' => [],
       '#weight' => -10,
-      '#error_no_message' => TRUE,
     ];
 
     if (!empty($fids) && $element['#files']) {
diff --git a/core/modules/file/src/Tests/FileFieldValidateTest.php b/core/modules/file/src/Tests/FileFieldValidateTest.php
index ab1d2a5..291c432 100644
--- a/core/modules/file/src/Tests/FileFieldValidateTest.php
+++ b/core/modules/file/src/Tests/FileFieldValidateTest.php
@@ -35,8 +35,7 @@ function testRequired() {
     $edit = array();
     $edit['title[0][value]'] = $this->randomMachineName();
     $this->drupalPostForm('node/add/' . $type_name, $edit, t('Save and publish'));
-    $this->assertText('1 error has been found:   ' . $field->label(), 'Node save failed when required file field was empty.');
-    $this->assertIdentical(1, count($this->xpath('//div[contains(concat(" ", normalize-space(@class), " "), :class)]//a', [':class' => ' messages--error '])), 'There is one link in the error message.');
+    $this->assertRaw(t('@title field is required.', array('@title' => $field->getLabel())), 'Node save failed when required file field was empty.');
 
     // Create a new node with the uploaded file.
     $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
@@ -57,8 +56,7 @@ function testRequired() {
     $edit = array();
     $edit['title[0][value]'] = $this->randomMachineName();
     $this->drupalPostForm('node/add/' . $type_name, $edit, t('Save and publish'));
-    $this->assertText('1 error has been found:   '  . $field->label(), 'Node save failed when required multiple value file field was empty.');
-    $this->assertIdentical(1, count($this->xpath('//div[contains(concat(" ", normalize-space(@class), " "), :class)]//a', [':class' => ' messages--error '])), 'There is one link in the error message.');
+    $this->assertRaw(t('@title field is required.', array('@title' => $field->getLabel())), 'Node save failed when required multiple value file field was empty.');
 
     // Create a new node with the uploaded file into the multivalue field.
     $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
diff --git a/core/modules/shortcut/src/Tests/ShortcutSetsTest.php b/core/modules/shortcut/src/Tests/ShortcutSetsTest.php
index 47efc0f..e9dc559 100644
--- a/core/modules/shortcut/src/Tests/ShortcutSetsTest.php
+++ b/core/modules/shortcut/src/Tests/ShortcutSetsTest.php
@@ -148,8 +148,7 @@ function testShortcutSetSwitchCreate() {
   function testShortcutSetSwitchNoSetName() {
     $edit = array('set' => 'new');
     $this->drupalPostForm('user/' . $this->adminUser->id() . '/shortcuts', $edit, t('Change set'));
-    $this->assertRaw('1 error has been found:');
-    $this->assertRaw('<a href="#edit-label">Label</a>');
+    $this->assertText(t('The new set label is required.'));
     $current_set = shortcut_current_displayed_set($this->adminUser);
     $this->assertEqual($current_set->id(), $this->set->id(), 'Attempting to switch to a new shortcut set without providing a set name does not succeed.');
     $this->assertFieldByXPath("//input[@name='label' and contains(concat(' ', normalize-space(@class), ' '), ' error ')]", NULL, 'The new set label field has the error class');
diff --git a/core/modules/system/src/Tests/Form/ElementTest.php b/core/modules/system/src/Tests/Form/ElementTest.php
index 1cc6cd8..85765ed 100644
--- a/core/modules/system/src/Tests/Form/ElementTest.php
+++ b/core/modules/system/src/Tests/Form/ElementTest.php
@@ -166,12 +166,4 @@ public function testFormAutocomplete() {
     $this->assertEqual(count($result), 1, 'Ensure that the user does have access to the autocompletion');
   }
 
-  /**
-   * Tests form element error messages.
-   */
-  public function testFormElementErrors() {
-    $this->drupalPostForm('form_test/details-form', [], 'Submit');
-    $this->assertText('I am an error on the details element.');
-  }
-
 }
diff --git a/core/modules/system/src/Tests/Form/FormTest.php b/core/modules/system/src/Tests/Form/FormTest.php
index 3ce775d..f0e5af0 100644
--- a/core/modules/system/src/Tests/Form/FormTest.php
+++ b/core/modules/system/src/Tests/Form/FormTest.php
@@ -190,7 +190,7 @@ function testRequiredCheckboxesRadio() {
     }
 
     // Check the page for error messages.
-    $errors = $this->xpath('//div[contains(@class, "form-item--error-message")]//strong');
+    $errors = $this->xpath('//div[contains(@class, "error")]//li');
     foreach ($errors as $error) {
       $expected_key = array_search($error[0], $expected);
       // If the error message is not one of the expected messages, fail.
diff --git a/core/modules/system/src/Tests/Form/TriggeringElementProgrammedUnitTest.php b/core/modules/system/src/Tests/Form/TriggeringElementProgrammedUnitTest.php
index f7cffa1..ae6b82c 100644
--- a/core/modules/system/src/Tests/Form/TriggeringElementProgrammedUnitTest.php
+++ b/core/modules/system/src/Tests/Form/TriggeringElementProgrammedUnitTest.php
@@ -19,23 +19,11 @@
  */
 class TriggeringElementProgrammedUnitTest extends KernelTestBase implements FormInterface {
 
-  /**
-   * {@inheritdoc}
-   */
   public static $modules = array('system');
 
   /**
    * {@inheritdoc}
    */
-  protected function setUp() {
-    parent::setUp();
-    $this->installSchema('system', ['router']);
-    \Drupal::service('router.builder')->rebuild();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function getFormId() {
     return 'triggering_element_programmed_form';
   }
diff --git a/core/modules/system/src/Tests/Form/ValidationTest.php b/core/modules/system/src/Tests/Form/ValidationTest.php
index 2bc4867..c73e674 100644
--- a/core/modules/system/src/Tests/Form/ValidationTest.php
+++ b/core/modules/system/src/Tests/Form/ValidationTest.php
@@ -8,7 +8,6 @@
 namespace Drupal\system\Tests\Form;
 
 use Drupal\Core\Render\Element;
-use Drupal\Core\Url;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -215,33 +214,17 @@ function testCustomRequiredError() {
     $edit = array();
     $this->drupalPostForm('form-test/validate-required', $edit, 'Submit');
 
-    $messages = [];
     foreach (Element::children($form) as $key) {
       if (isset($form[$key]['#required_error'])) {
         $this->assertNoText(t('@name field is required.', array('@name' => $form[$key]['#title'])));
-        $messages[] = [
-          'title' => $form[$key]['#title'],
-          'message' => $form[$key]['#required_error'],
-          'key' => $key,
-        ];
+        $this->assertText($form[$key]['#required_error']);
       }
       elseif (isset($form[$key]['#form_test_required_error'])) {
         $this->assertNoText(t('@name field is required.', array('@name' => $form[$key]['#title'])));
-        $messages[] = [
-          'title' => $form[$key]['#title'],
-          'message' => $form[$key]['#form_test_required_error'],
-          'key' => $key,
-        ];
-      }
-      elseif (!empty($form[$key]['#required'])) {
-        $messages[] = [
-          'title' => $form[$key]['#title'],
-          'message' => t('@name field is required.', ['@name' => $form[$key]['#title']]),
-          'key' => $key,
-        ];
+        $this->assertText($form[$key]['#form_test_required_error']);
       }
     }
-    $this->assertErrorMessages($messages);
+    $this->assertNoText(t('An illegal choice has been detected. Please contact the site administrator.'));
 
     // Verify that no custom validation error appears with valid values.
     $edit = array(
@@ -251,7 +234,6 @@ function testCustomRequiredError() {
     );
     $this->drupalPostForm('form-test/validate-required', $edit, 'Submit');
 
-    $messages = [];
     foreach (Element::children($form) as $key) {
       if (isset($form[$key]['#required_error'])) {
         $this->assertNoText(t('@name field is required.', array('@name' => $form[$key]['#title'])));
@@ -261,50 +243,8 @@ function testCustomRequiredError() {
         $this->assertNoText(t('@name field is required.', array('@name' => $form[$key]['#title'])));
         $this->assertNoText($form[$key]['#form_test_required_error']);
       }
-      elseif (!empty($form[$key]['#required'])) {
-        $messages[] = [
-          'title' => $form[$key]['#title'],
-          'message' => t('@name field is required.', ['@name' => $form[$key]['#title']]),
-          'key' => $key,
-        ];
-      }
     }
-    $this->assertErrorMessages($messages);
-  }
-
-  /**
-   * Asserts that the given error messages are displayed.
-   *
-   * @param array $messages
-   *   An associative array of error messages keyed by the order they appear on
-   *   the page, with the following key-value pairs:
-   *   - title: The human readable form element title.
-   *   - message: The error message for this form element.
-   *   - key: The key used for the form element.
-   */
-  protected function assertErrorMessages($messages) {
-    $element = $this->xpath('//div[@class = "form-item--error-message"]/strong');
-    $this->assertIdentical(count($messages), count($element));
-
-    $error_links = [];
-    foreach ($messages as $delta => $message) {
-      // Ensure the message appears in the correct place.
-      if (!isset($element[$delta])) {
-        $this->fail(format_string('The error message for the "@title" element with key "@key" was not found.', ['@title' => $message['title'], '@key' => $message['key']]));
-      }
-      else {
-        $this->assertEqual($message['message'], (string) $element[$delta]);
-      }
-
-      // Gather the element for checking the jump link section.
-      $error_links[] = \Drupal::l($message['title'], Url::fromRoute('<none>', [], ['fragment' => 'edit-' . str_replace('_', '-', $message['key']), 'external' => TRUE]));
-    }
-    $top_message = \Drupal::translation()->formatPlural(count($error_links), '1 error has been found:', '@count errors have been found:');
-    $this->assertRaw($top_message);
-    foreach ($error_links as $error_link) {
-      $this->assertRaw($error_link);
-    }
-    $this->assertNoText('An illegal choice has been detected. Please contact the site administrator.');
+    $this->assertNoText(t('An illegal choice has been detected. Please contact the site administrator.'));
   }
 
 }
diff --git a/core/modules/system/templates/datetime-wrapper.html.twig b/core/modules/system/templates/datetime-wrapper.html.twig
index 8430baa..7b054c2 100644
--- a/core/modules/system/templates/datetime-wrapper.html.twig
+++ b/core/modules/system/templates/datetime-wrapper.html.twig
@@ -25,9 +25,4 @@
   <h4{{ title_attributes.addClass(title_classes) }}>{{ title }}</h4>
 {% endif %}
 {{ content }}
-{% if errors %}
-  <div>
-    {{ errors }}
-  </div>
-{% endif %}
 {{ description }}
diff --git a/core/modules/system/templates/details.html.twig b/core/modules/system/templates/details.html.twig
index cf50eb0..1c0fd79 100644
--- a/core/modules/system/templates/details.html.twig
+++ b/core/modules/system/templates/details.html.twig
@@ -5,7 +5,6 @@
  *
  * Available variables
  * - attributes: A list of HTML attributes for the details element.
- * - errors: (optional) Any errors for this details element, may not be set.
  * - title: (optional) The title of the element, may not be set.
  * - description: (optional) The description of the element, may not be set.
  * - children: (optional) The children of the element, may not be set.
@@ -21,12 +20,6 @@
     <summary{{ summary_attributes }}>{{ title }}</summary>
   {%- endif -%}
 
-  {% if errors %}
-    <div>
-      {{ errors }}
-    </div>
-  {% endif %}
-
   {{ description }}
   {{ children }}
   {{ value }}
diff --git a/core/modules/system/templates/fieldset.html.twig b/core/modules/system/templates/fieldset.html.twig
index b67ec85..65533ec 100644
--- a/core/modules/system/templates/fieldset.html.twig
+++ b/core/modules/system/templates/fieldset.html.twig
@@ -5,7 +5,6 @@
  *
  * Available variables:
  * - attributes: HTML attributes for the fieldset element.
- * - errors: (optional) Any errors for this fieldset element, may not be set.
  * - required: Boolean indicating whether the fieldeset element is required.
  * - legend: The legend element containing the following properties:
  *   - title: Title of the fieldset, intended for use as the text of the legend.
@@ -43,11 +42,6 @@
     <span{{ legend_span.attributes.addClass(legend_span_classes) }}>{{ legend.title }}</span>
   </legend>
   <div class="fieldset-wrapper">
-    {% if errors %}
-      <div>
-        {{ errors }}
-      </div>
-    {% endif %}
     {% if prefix %}
       <span class="field-prefix">{{ prefix }}</span>
     {% endif %}
diff --git a/core/modules/system/templates/form-element.html.twig b/core/modules/system/templates/form-element.html.twig
index 3311ebc..abf1bf6 100644
--- a/core/modules/system/templates/form-element.html.twig
+++ b/core/modules/system/templates/form-element.html.twig
@@ -5,7 +5,6 @@
  *
  * Available variables:
  * - attributes: HTML attributes for the containing element.
- * - errors: (optional) Any errors for this form element, may not be set.
  * - prefix: (optional) The form element prefix, may not be set.
  * - suffix: (optional) The form element suffix, may not be set.
  * - required: The required marker, or empty if the associated form element is
@@ -55,7 +54,6 @@
     'js-form-item-' ~ name|clean_class,
     title_display not in ['after', 'before'] ? 'form-no-label',
     disabled == 'disabled' ? 'form-disabled',
-    errors ? 'form-item--error',
   ]
 %}
 {%
@@ -83,11 +81,6 @@
   {% if label_display == 'after' %}
     {{ label }}
   {% endif %}
-  {% if errors %}
-    <div class="form-item--error-message">
-      {{ errors }}
-    </div>
-  {% endif %}
   {% if description_display in ['after', 'invisible'] and description.content %}
     <div{{ description.attributes.addClass(description_classes) }}>
       {{ description.content }}
diff --git a/core/modules/system/tests/modules/form_test/form_test.routing.yml b/core/modules/system/tests/modules/form_test/form_test.routing.yml
index d8c5833..d070aba 100644
--- a/core/modules/system/tests/modules/form_test/form_test.routing.yml
+++ b/core/modules/system/tests/modules/form_test/form_test.routing.yml
@@ -402,14 +402,6 @@ form_test.button_class:
   requirements:
     _access: 'TRUE'
 
-form_test.details_form:
-  path: '/form_test/details-form'
-  defaults:
-    _form: '\Drupal\form_test\Form\FormTestDetailsForm'
-    _title: 'Form details form test'
-  requirements:
-    _access: 'TRUE'
-
 form_test.description_display:
   path: '/form_test/form-descriptions'
   defaults:
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestDetailsForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestDetailsForm.php
deleted file mode 100644
index 8944a4c..0000000
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestDetailsForm.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\form_test\Form\FormTestGroupContainerForm.
- */
-
-namespace Drupal\form_test\Form;
-
-use Drupal\Core\Form\FormBase;
-use Drupal\Core\Form\FormStateInterface;
-
-/**
- * Builds a simple form to test the #group property on #type 'container'.
- */
-class FormTestDetailsForm extends FormBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'form_test_details_form';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state) {
-    $form['meta'] = [
-      '#type' => 'details',
-      '#title' => 'Details element',
-      '#open' => TRUE,
-    ];
-    $form['submit'] = ['#type' => 'submit', '#value' => 'Submit'];
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function validateForm(array &$form, FormStateInterface $form_state) {
-    $form_state->setErrorByName('meta', 'I am an error on the details element.');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-  }
-
-}
diff --git a/core/modules/user/src/Tests/UserBlocksTest.php b/core/modules/user/src/Tests/UserBlocksTest.php
index f16e31e..3da2d46 100644
--- a/core/modules/user/src/Tests/UserBlocksTest.php
+++ b/core/modules/user/src/Tests/UserBlocksTest.php
@@ -67,15 +67,6 @@ function testUserLoginBlockVisibility() {
    * Test the user login block.
    */
   function testUserLoginBlock() {
-    // Make sure the validation error is displayed when try to login with
-    // invalid username/password.
-    $edit['name'] = $this->randomMachineName();
-    $edit['pass'] = $this->randomMachineName();
-    $this->drupalPostForm('node', $edit, t('Log in'));
-    $this->assertRaw('1 error has been found:');
-    $this->assertRaw('<a href="#edit-name">Username</a>');
-    $this->assertText(t('Sorry, unrecognized username or password.'));
-
     // Create a user with some permission that anonymous users lack.
     $user = $this->drupalCreateUser(array('administer permissions'));
 
diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
index e5f2963..f880a65 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
@@ -491,11 +491,15 @@ public function testUniqueHtmlId() {
       ->method('buildForm')
       ->will($this->returnValue($expected_form));
 
-    $form_state = new FormState();
+    $form_state = $this->getMockBuilder('Drupal\Core\Form\FormState')
+      ->setMethods(array('drupalSetMessage'))
+      ->getMock();
     $form = $this->simulateFormSubmission($form_id, $form_arg, $form_state);
     $this->assertSame('test-form-id', $form['#id']);
 
-    $form_state = new FormState();
+    $form_state = $this->getMockBuilder('Drupal\Core\Form\FormState')
+      ->setMethods(array('drupalSetMessage'))
+      ->getMock();
     $form = $this->simulateFormSubmission($form_id, $form_arg, $form_state);
     $this->assertSame('test-form-id--2', $form['#id']);
   }
diff --git a/core/tests/Drupal/Tests/Core/Form/FormElementHelperTest.php b/core/tests/Drupal/Tests/Core/Form/FormElementHelperTest.php
deleted file mode 100644
index d25a6f5..0000000
--- a/core/tests/Drupal/Tests/Core/Form/FormElementHelperTest.php
+++ /dev/null
@@ -1,179 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Tests\Core\Form\FormElementHelperTest.
- */
-
-namespace Drupal\Tests\Core\Form;
-
-use Drupal\Core\Form\FormElementHelper;
-use Drupal\Tests\UnitTestCase;
-
-/**
- * Tests the form element helper.
- *
- * @group Drupal
- * @group Form
- *
- * @coversDefaultClass \Drupal\Core\Form\FormElementHelper
- */
-class FormElementHelperTest extends UnitTestCase {
-
-  /**
-   * Tests the getElementByName() method.
-   *
-   * @covers ::getElementByName
-   *
-   * @dataProvider getElementByNameProvider
-   */
-  public function testGetElementByName($name, $form, $expected) {
-    $this->assertSame($expected, FormElementHelper::getElementByName($name, $form));
-  }
-
-  /**
-   * Provides test data.
-   */
-  public function getElementByNameProvider() {
-    $data = [];
-    $data[] = ['id', [], []];
-    $data[] = [
-      'id',
-      [
-        'id' => [
-          '#title' => 'ID',
-          '#parents' => ['id'],
-        ],
-      ],
-      [
-        '#title' => 'ID',
-        '#parents' => ['id'],
-      ],
-    ];
-    $data[] = [
-      'id',
-      [
-        'fieldset' => [
-          'id' => [
-            '#title' => 'ID',
-            '#parents' => ['id'],
-          ],
-          '#parents' => ['fieldset'],
-        ],
-      ],
-      [
-        '#title' => 'ID',
-        '#parents' => ['id'],
-      ],
-    ];
-    $data[] = [
-      'fieldset',
-      [
-        'fieldset' => [
-          'id' => [
-            '#title' => 'ID',
-            '#parents' => ['id'],
-          ],
-          '#parents' => ['fieldset'],
-        ],
-      ],
-      [
-        'id' => [
-          '#title' => 'ID',
-          '#parents' => ['id'],
-        ],
-        '#parents' => ['fieldset'],
-      ],
-    ];
-    $data[] = [
-      'fieldset][id',
-      [
-        'fieldset' => [
-          '#tree' => TRUE,
-          'id' => [
-            '#title' => 'ID',
-            '#parents' => ['fieldset', 'id'],
-          ],
-          '#parents' => ['fieldset'],
-        ],
-      ],
-      [
-        '#title' => 'ID',
-        '#parents' => ['fieldset', 'id'],
-      ],
-    ];
-    return $data;
-  }
-
-  /**
-   * Tests the getElementTitle() method.
-   *
-   * @covers ::getElementTitle
-   *
-   * @dataProvider getElementTitleProvider
-   */
-  public function testGetElementTitle($name, $form, $expected) {
-    $element = FormElementHelper::getElementByName($name, $form);
-    $this->assertSame($expected, FormElementHelper::getElementTitle($element));
-  }
-
-  /**
-   * Provides test data.
-   */
-  public function getElementTitleProvider() {
-    $data = [];
-    $data[] = ['id', [], ''];
-    $data[] = [
-      'id',
-      [
-        'id' => [
-          '#title' => 'ID',
-          '#parents' => ['id'],
-        ],
-      ],
-      'ID',
-    ];
-    $data[] = [
-      'id',
-      [
-        'fieldset' => [
-          'id' => [
-            '#title' => 'ID',
-            '#parents' => ['id'],
-          ],
-          '#parents' => ['fieldset'],
-        ],
-      ],
-      'ID',
-    ];
-    $data[] = [
-      'fieldset',
-      [
-        'fieldset' => [
-          'id' => [
-            '#title' => 'ID',
-            '#parents' => ['id'],
-          ],
-          '#parents' => ['fieldset'],
-        ],
-      ],
-      'ID',
-    ];
-    $data[] = [
-      'fieldset][id',
-      [
-        'fieldset' => [
-          '#tree' => TRUE,
-          'id' => [
-            '#title' => 'ID',
-            '#parents' => ['fieldset', 'id'],
-          ],
-          '#parents' => ['fieldset'],
-        ],
-      ],
-      'ID',
-    ];
-    return $data;
-  }
-
-}
diff --git a/core/tests/Drupal/Tests/Core/Form/FormErrorHandlerTest.php b/core/tests/Drupal/Tests/Core/Form/FormErrorHandlerTest.php
deleted file mode 100644
index cd8e3b2..0000000
--- a/core/tests/Drupal/Tests/Core/Form/FormErrorHandlerTest.php
+++ /dev/null
@@ -1,132 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Tests\Core\Form\FormErrorHandlerTest.
- */
-
-namespace Drupal\Tests\Core\Form;
-
-use Drupal\Core\Form\FormState;
-use Drupal\Tests\UnitTestCase;
-
-/**
- * @coversDefaultClass \Drupal\Core\Form\FormErrorHandler
- * @group Form
- */
-class FormErrorHandlerTest extends UnitTestCase {
-
-  /**
-   * @covers ::handleFormErrors
-   * @covers ::displayErrorMessages
-   */
-  public function testDisplayErrorMessages() {
-    $link_generator = $this->getMock('Drupal\Core\Utility\LinkGeneratorInterface');
-    $link_generator->expects($this->any())
-      ->method('generate')
-      ->willReturnArgument(0);
-    $renderer = $this->getMock('\Drupal\Core\Render\RendererInterface');
-    $form_error_handler = $this->getMockBuilder('Drupal\Core\Form\FormErrorHandler')
-      ->setConstructorArgs([$this->getStringTranslationStub(), $link_generator, $renderer])
-      ->setMethods(['drupalSetMessage'])
-      ->getMock();
-
-    $form_error_handler->expects($this->at(0))
-      ->method('drupalSetMessage')
-      ->with('no title given', 'error');
-    $form_error_handler->expects($this->at(1))
-      ->method('drupalSetMessage')
-      ->with('element is invisible', 'error');
-    $form_error_handler->expects($this->at(2))
-      ->method('drupalSetMessage')
-      ->with('this missing element is invalid', 'error');
-    $form_error_handler->expects($this->at(3))
-      ->method('drupalSetMessage')
-      ->with('3 errors have been found: <ul-comma-list-mock><li-mock>Test 1</li-mock><li-mock>Test 2 &amp; a half</li-mock><li-mock>Test 3</li-mock></ul-comma-list-mock>', 'error');
-
-    $renderer->expects($this->any())
-      ->method('renderPlain')
-      ->will($this->returnCallback(function ($render_array) {
-        return $render_array[0]['#markup'] . '<ul-comma-list-mock><li-mock>' . implode(array_map('htmlspecialchars', $render_array[1]['#items']), '</li-mock><li-mock>') . '</li-mock></ul-comma-list-mock>';
-      }));
-
-    $form = [
-      '#parents' => [],
-    ];
-    $form['test1'] = [
-      '#type' => 'textfield',
-      '#title' => 'Test 1',
-      '#parents' => ['test1'],
-      '#id' => 'edit-test1',
-    ];
-    $form['test2'] = [
-      '#type' => 'textfield',
-      '#title' => 'Test 2 & a half',
-      '#parents' => ['test2'],
-      '#id' => 'edit-test2',
-    ];
-    $form['fieldset'] = [
-      '#parents' => ['fieldset'],
-      'test3' => [
-        '#type' => 'textfield',
-        '#title' => 'Test 3',
-        '#parents' => ['fieldset', 'test3'],
-        '#id' => 'edit-test3',
-      ],
-    ];
-    $form['test4'] = [
-      '#type' => 'textfield',
-      '#title' => 'Test 4',
-      '#parents' => ['test4'],
-      '#id' => 'edit-test4',
-      '#error_no_message' => TRUE,
-    ];
-    $form['test5'] = [
-      '#type' => 'textfield',
-      '#parents' => ['test5'],
-      '#id' => 'edit-test5',
-    ];
-    $form['test6'] = [
-      '#type' => 'value',
-      '#title' => 'Test 6',
-      '#parents' => ['test6'],
-      '#id' => 'edit-test6',
-    ];
-    $form_state = new FormState();
-    $form_state->setErrorByName('test1', 'invalid');
-    $form_state->setErrorByName('test2', 'invalid');
-    $form_state->setErrorByName('fieldset][test3', 'invalid');
-    $form_state->setErrorByName('test4', 'no error message');
-    $form_state->setErrorByName('test5', 'no title given');
-    $form_state->setErrorByName('test6', 'element is invisible');
-    $form_state->setErrorByName('missing_element', 'this missing element is invalid');
-    $form_error_handler->handleFormErrors($form, $form_state);
-    $this->assertSame('invalid', $form['test1']['#errors']);
-  }
-
-  /**
-   * @covers ::handleFormErrors
-   * @covers ::setElementErrorsFromFormState
-   */
-  public function testSetElementErrorsFromFormState() {
-    $form_error_handler = $this->getMockBuilder('Drupal\Core\Form\FormErrorHandler')
-      ->setConstructorArgs([$this->getStringTranslationStub(), $this->getMock('Drupal\Core\Utility\LinkGeneratorInterface'), $this->getMock('\Drupal\Core\Render\RendererInterface')])
-      ->setMethods(['drupalSetMessage'])
-      ->getMock();
-
-    $form = [
-      '#parents' => [],
-    ];
-    $form['test'] = [
-      '#type' => 'textfield',
-      '#title' => 'Test',
-      '#parents' => ['test'],
-      '#id' => 'edit-test',
-    ];
-    $form_state = new FormState();
-    $form_state->setErrorByName('test', 'invalid');
-    $form_error_handler->handleFormErrors($form, $form_state);
-    $this->assertSame('invalid', $form['test']['#errors']);
-  }
-
-}
diff --git a/core/tests/Drupal/Tests/Core/Form/FormStateTest.php b/core/tests/Drupal/Tests/Core/Form/FormStateTest.php
index 24904e3..b361e13 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormStateTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormStateTest.php
@@ -62,10 +62,15 @@ public function providerTestGetRedirect() {
    * @covers ::setError
    */
   public function testSetError() {
-    $form_state = new FormState();
+    $form_state = $this->getMockBuilder('Drupal\Core\Form\FormState')
+      ->setMethods(array('drupalSetMessage'))
+      ->getMock();
+    $form_state->expects($this->once())
+      ->method('drupalSetMessage')
+      ->willReturn('Fail');
+
     $element['#parents'] = array('foo', 'bar');
     $form_state->setError($element, 'Fail');
-    $this->assertSame(['foo][bar' => 'Fail'], $form_state->getErrors());
   }
 
   /**
@@ -103,10 +108,14 @@ public function providerTestGetError() {
    *
    * @dataProvider providerTestSetErrorByName
    */
-  public function testSetErrorByName($limit_validation_errors, $expected_errors) {
-    $form_state = new FormState();
+  public function testSetErrorByName($limit_validation_errors, $expected_errors, $set_message = FALSE) {
+    $form_state = $this->getMockBuilder('Drupal\Core\Form\FormState')
+      ->setMethods(array('drupalSetMessage'))
+      ->getMock();
     $form_state->setLimitValidationErrors($limit_validation_errors);
     $form_state->clearErrors();
+    $form_state->expects($set_message ? $this->once() : $this->never())
+      ->method('drupalSetMessage');
 
     $form_state->setErrorByName('test', 'Fail 1');
     $form_state->setErrorByName('test', 'Fail 2');
@@ -122,7 +131,7 @@ public function providerTestSetErrorByName() {
       array(array(array('options')), array('options' => '')),
       // Do not limit an validation, and, ensuring the first error is returned
       // for the 'test' element.
-      [NULL, ['test' => 'Fail 1', 'options' => '']],
+      array(NULL, array('test' => 'Fail 1', 'options' => ''), TRUE),
       // Limit all validation.
       array(array(), array()),
     );
@@ -137,7 +146,9 @@ public function providerTestSetErrorByName() {
    * @expectedExceptionMessage Form errors cannot be set after form validation has finished.
    */
   public function testFormErrorsDuringSubmission() {
-    $form_state = new FormState();
+    $form_state = $this->getMockBuilder('Drupal\Core\Form\FormState')
+      ->setMethods(array('drupalSetMessage'))
+      ->getMock();
     $form_state->setValidationComplete();
     $form_state->setErrorByName('test', 'message');
   }
diff --git a/core/tests/Drupal/Tests/Core/Form/FormTestBase.php b/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
index 83aec87..5bb22e3 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
@@ -183,10 +183,9 @@ protected function setUp() {
     $this->requestStack = new RequestStack();
     $this->requestStack->push($this->request);
     $this->logger = $this->getMock('Drupal\Core\Logger\LoggerChannelInterface');
-    $form_error_handler = $this->getMock('Drupal\Core\Form\FormErrorHandlerInterface');
     $this->formValidator = $this->getMockBuilder('Drupal\Core\Form\FormValidator')
-      ->setConstructorArgs([$this->requestStack, $this->getStringTranslationStub(), $this->csrfToken, $this->logger, $form_error_handler])
-      ->setMethods(NULL)
+      ->setConstructorArgs(array($this->requestStack, $this->getStringTranslationStub(), $this->csrfToken, $this->logger))
+      ->setMethods(array('drupalSetMessage'))
       ->getMock();
     $this->formSubmitter = $this->getMockBuilder('Drupal\Core\Form\FormSubmitter')
       ->setConstructorArgs(array($this->requestStack, $this->urlGenerator))
diff --git a/core/tests/Drupal/Tests/Core/Form/FormValidatorTest.php b/core/tests/Drupal/Tests/Core/Form/FormValidatorTest.php
index 5fe5a09..83c1007 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormValidatorTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormValidatorTest.php
@@ -19,39 +19,6 @@
 class FormValidatorTest extends UnitTestCase {
 
   /**
-   * A logger instance.
-   *
-   * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $logger;
-
-  /**
-   * The CSRF token generator to validate the form token.
-   *
-   * @var \Drupal\Core\Access\CsrfTokenGenerator|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $csrfToken;
-
-  /**
-   * The form error handler.
-   *
-   * @var \Drupal\Core\Form\FormErrorHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $formErrorHandler;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function setUp() {
-    parent::setUp();
-    $this->logger = $this->getMock('Psr\Log\LoggerInterface');
-    $this->csrfToken = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator')
-      ->disableOriginalConstructor()
-      ->getMock();
-    $this->formErrorHandler = $this->getMock('Drupal\Core\Form\FormErrorHandlerInterface');
-  }
-
-  /**
    * Tests the 'validation_complete' $form_state flag.
    *
    * @covers ::validateForm
@@ -59,7 +26,7 @@ protected function setUp() {
    */
   public function testValidationComplete() {
     $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator')
-      ->setConstructorArgs([new RequestStack(), $this->getStringTranslationStub(), $this->csrfToken, $this->logger, $this->formErrorHandler])
+      ->disableOriginalConstructor()
       ->setMethods(NULL)
       ->getMock();
 
@@ -77,7 +44,7 @@ public function testValidationComplete() {
    */
   public function testPreventDuplicateValidation() {
     $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator')
-      ->setConstructorArgs([new RequestStack(), $this->getStringTranslationStub(), $this->csrfToken, $this->logger, $this->formErrorHandler])
+      ->disableOriginalConstructor()
       ->setMethods(array('doValidateForm'))
       ->getMock();
     $form_validator->expects($this->never())
@@ -97,19 +64,18 @@ public function testPreventDuplicateValidation() {
    */
   public function testMustValidate() {
     $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator')
-      ->setConstructorArgs([new RequestStack(), $this->getStringTranslationStub(), $this->csrfToken, $this->logger, $this->formErrorHandler])
+      ->disableOriginalConstructor()
       ->setMethods(array('doValidateForm'))
       ->getMock();
     $form_validator->expects($this->once())
       ->method('doValidateForm');
-    $this->formErrorHandler->expects($this->once())
-      ->method('handleFormErrors');
 
     $form = array();
     $form_state = (new FormState())
       ->setValidationComplete()
       ->setValidationEnforced();
     $form_validator->validateForm('test_form_id', $form, $form_state);
+    $this->assertArrayHasKey('#errors', $form);
   }
 
   /**
@@ -119,12 +85,16 @@ public function testValidateInvalidFormToken() {
     $request_stack = new RequestStack();
     $request = new Request(array(), array(), array(), array(), array(), array('REQUEST_URI' => '/test/example?foo=bar'));
     $request_stack->push($request);
-    $this->csrfToken->expects($this->once())
+    $csrf_token = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $csrf_token->expects($this->once())
       ->method('validate')
       ->will($this->returnValue(FALSE));
+    $logger = $this->getMock('Psr\Log\LoggerInterface');
 
     $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator')
-      ->setConstructorArgs([$request_stack, $this->getStringTranslationStub(), $this->csrfToken, $this->logger, $this->formErrorHandler])
+      ->setConstructorArgs(array($request_stack, $this->getStringTranslationStub(), $csrf_token, $logger))
       ->setMethods(array('doValidateForm'))
       ->getMock();
     $form_validator->expects($this->never())
@@ -147,12 +117,16 @@ public function testValidateInvalidFormToken() {
    */
   public function testValidateValidFormToken() {
     $request_stack = new RequestStack();
-    $this->csrfToken->expects($this->once())
+    $csrf_token = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $csrf_token->expects($this->once())
       ->method('validate')
       ->will($this->returnValue(TRUE));
+    $logger = $this->getMock('Psr\Log\LoggerInterface');
 
     $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator')
-      ->setConstructorArgs([$request_stack, $this->getStringTranslationStub(), $this->csrfToken, $this->logger, $this->formErrorHandler])
+      ->setConstructorArgs(array($request_stack, $this->getStringTranslationStub(), $csrf_token, $logger))
       ->setMethods(array('doValidateForm'))
       ->getMock();
     $form_validator->expects($this->once())
@@ -170,13 +144,38 @@ public function testValidateValidFormToken() {
   }
 
   /**
+   * @covers ::setElementErrorsFromFormState
+   */
+  public function testSetElementErrorsFromFormState() {
+    $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator')
+      ->disableOriginalConstructor()
+      ->setMethods(NULL)
+      ->getMock();
+
+    $form = array(
+      '#parents' => array(),
+    );
+    $form['test'] = array(
+      '#type' => 'textfield',
+      '#title' => 'Test',
+      '#parents' => array('test'),
+    );
+    $form_state = $this->getMockBuilder('Drupal\Core\Form\FormState')
+      ->setMethods(array('drupalSetMessage'))
+      ->getMock();
+    $form_state->setErrorByName('test', 'invalid');
+    $form_validator->validateForm('test_form_id', $form, $form_state);
+    $this->assertSame('invalid', $form['test']['#errors']);
+  }
+
+  /**
    * @covers ::handleErrorsWithLimitedValidation
    *
    * @dataProvider providerTestHandleErrorsWithLimitedValidation
    */
   public function testHandleErrorsWithLimitedValidation($sections, $triggering_element, $values, $expected) {
     $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator')
-      ->setConstructorArgs([new RequestStack(), $this->getStringTranslationStub(), $this->csrfToken, $this->logger, $this->formErrorHandler])
+      ->disableOriginalConstructor()
       ->setMethods(NULL)
       ->getMock();
 
@@ -272,7 +271,7 @@ public function providerTestHandleErrorsWithLimitedValidation() {
    */
   public function testExecuteValidateHandlers() {
     $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator')
-      ->setConstructorArgs([new RequestStack(), $this->getStringTranslationStub(), $this->csrfToken, $this->logger, $this->formErrorHandler])
+      ->disableOriginalConstructor()
       ->setMethods(NULL)
       ->getMock();
     $mock = $this->getMock('stdClass', array('validate_handler', 'hash_validate'));
@@ -302,8 +301,13 @@ public function testExecuteValidateHandlers() {
    * @dataProvider providerTestRequiredErrorMessage
    */
   public function testRequiredErrorMessage($element, $expected_message) {
+    $csrf_token = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $logger = $this->getMock('Psr\Log\LoggerInterface');
+
     $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator')
-      ->setConstructorArgs([new RequestStack(), $this->getStringTranslationStub(), $this->csrfToken, $this->logger, $this->formErrorHandler])
+      ->setConstructorArgs(array(new RequestStack(), $this->getStringTranslationStub(), $csrf_token, $logger))
       ->setMethods(array('executeValidateHandlers'))
       ->getMock();
     $form_validator->expects($this->once())
@@ -351,7 +355,7 @@ public function providerTestRequiredErrorMessage() {
    */
   public function testElementValidate() {
     $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator')
-      ->setConstructorArgs([new RequestStack(), $this->getStringTranslationStub(), $this->csrfToken, $this->logger, $this->formErrorHandler])
+      ->disableOriginalConstructor()
       ->setMethods(array('executeValidateHandlers'))
       ->getMock();
     $form_validator->expects($this->once())
@@ -378,13 +382,18 @@ public function testElementValidate() {
    * @dataProvider providerTestPerformRequiredValidation
    */
   public function testPerformRequiredValidation($element, $expected_message, $call_watchdog) {
+    $csrf_token = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $logger = $this->getMock('Psr\Log\LoggerInterface');
+
     $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator')
-      ->setConstructorArgs([new RequestStack(), $this->getStringTranslationStub(), $this->csrfToken, $this->logger, $this->formErrorHandler])
+      ->setConstructorArgs(array(new RequestStack(), $this->getStringTranslationStub(), $csrf_token, $logger))
       ->setMethods(array('setError'))
       ->getMock();
 
     if ($call_watchdog) {
-      $this->logger->expects($this->once())
+      $logger->expects($this->once())
         ->method('error')
         ->with($this->isType('string'), $this->isType('array'));
     }
diff --git a/core/themes/bartik/css/components/form.css b/core/themes/bartik/css/components/form.css
index c157303..6df6077 100644
--- a/core/themes/bartik/css/components/form.css
+++ b/core/themes/bartik/css/components/form.css
@@ -270,13 +270,3 @@ input.form-submit:focus {
   margin-left: 0.6em;
   margin-right: 0;
 }
-
-/* Form error styles. */
-.form-item textarea.error + .cke {
-  border: 2px solid red;
-}
-
-/* Form error message styles. */
-.form-item--error-message {
-  color: #e32700;
-}
diff --git a/core/themes/classy/css/components/form.css b/core/themes/classy/css/components/form.css
index 7ec4566..708ff32 100644
--- a/core/themes/classy/css/components/form.css
+++ b/core/themes/classy/css/components/form.css
@@ -85,14 +85,3 @@ abbr.ajax-changed {
 .form-item select.error {
   border: 2px solid red;
 }
-
-/* Inline error messages. */
-.form-item--error-message:before {
-  content: '';
-  display: inline-block;
-  height: 14px;
-  width: 14px;
-  vertical-align: sub;
-  background: url(../../../../misc/icons/e32700/error.svg) no-repeat;
-  background-size: contain;
-}
diff --git a/core/themes/classy/templates/form/datetime-wrapper.html.twig b/core/themes/classy/templates/form/datetime-wrapper.html.twig
index 3f6aa59..e3f504b 100644
--- a/core/themes/classy/templates/form/datetime-wrapper.html.twig
+++ b/core/themes/classy/templates/form/datetime-wrapper.html.twig
@@ -24,11 +24,6 @@
   <h4{{ title_attributes.addClass(title_classes) }}>{{ title }}</h4>
 {% endif %}
 {{ content }}
-{% if errors %}
-  <div class="form-item--error-message">
-    <strong>{{ errors }}</strong>
-  </div>
-{% endif %}
 {% if description %}
   <div class="description">{{ description }}</div>
 {% endif %}
diff --git a/core/themes/classy/templates/form/details.html.twig b/core/themes/classy/templates/form/details.html.twig
index a4ce6f6..9b149d7 100644
--- a/core/themes/classy/templates/form/details.html.twig
+++ b/core/themes/classy/templates/form/details.html.twig
@@ -5,7 +5,6 @@
  *
  * Available variables
  * - attributes: A list of HTML attributes for the details element.
- * - errors: (optional) Any errors for this details element, may not be set.
  * - title: (optional) The title of the element, may not be set.
  * - description: (optional) The description of the element, may not be set.
  * - children: (optional) The children of the element, may not be set.
@@ -19,11 +18,6 @@
     <summary{{ summary_attributes }}>{{ title }}</summary>
   {%- endif -%}
   <div class="details-wrapper">
-    {% if errors %}
-      <div class="form-item--error-message">
-        <strong>{{ errors }}</strong>
-      </div>
-    {% endif %}
     {%- if description -%}
       <div class="details-description">{{ description }}</div>
     {%- endif -%}
diff --git a/core/themes/classy/templates/form/fieldset.html.twig b/core/themes/classy/templates/form/fieldset.html.twig
index 4bd7d0a..ee628d2 100644
--- a/core/themes/classy/templates/form/fieldset.html.twig
+++ b/core/themes/classy/templates/form/fieldset.html.twig
@@ -5,7 +5,6 @@
  *
  * Available variables:
  * - attributes: HTML attributes for the fieldset element.
- * - errors: (optional) Any errors for this fieldset element, may not be set.
  * - required: Boolean indicating whether the fieldeset element is required.
  * - legend: The legend element containing the following properties:
  *   - title: Title of the fieldset, intended for use as the text of the legend.
@@ -41,11 +40,6 @@
     <span{{ legend_span.attributes.addClass(legend_span_classes) }}>{{ legend.title }}</span>
   </legend>
   <div class="fieldset-wrapper">
-    {% if errors %}
-      <div class="form-item--error-message">
-        <strong>{{ errors }}</strong>
-      </div>
-    {% endif %}
     {% if prefix %}
       <span class="field-prefix">{{ prefix }}</span>
     {% endif %}
diff --git a/core/themes/classy/templates/form/form-element.html.twig b/core/themes/classy/templates/form/form-element.html.twig
index 3bde4f7..3c0af26 100644
--- a/core/themes/classy/templates/form/form-element.html.twig
+++ b/core/themes/classy/templates/form/form-element.html.twig
@@ -5,7 +5,6 @@
  *
  * Available variables:
  * - attributes: HTML attributes for the containing element.
- * - errors: (optional) Any errors for this form element, may not be set.
  * - prefix: (optional) The form element prefix, may not be set.
  * - suffix: (optional) The form element suffix, may not be set.
  * - required: The required marker, or empty if the associated form element is
@@ -54,7 +53,6 @@
     'form-item-' ~ name|clean_class,
     title_display not in ['after', 'before'] ? 'form-no-label',
     disabled == 'disabled' ? 'form-disabled',
-    errors ? 'form-item--error',
   ]
 %}
 {%
@@ -82,11 +80,6 @@
   {% if label_display == 'after' %}
     {{ label }}
   {% endif %}
-  {% if errors %}
-    <div class="form-item--error-message">
-      <strong>{{ errors }}</strong>
-    </div>
-  {% endif %}
   {% if description_display in ['after', 'invisible'] and description.content %}
     <div{{ description.attributes.addClass(description_classes) }}>
       {{ description.content }}
diff --git a/core/themes/seven/css/components/form.css b/core/themes/seven/css/components/form.css
index f74eb74..111a728 100644
--- a/core/themes/seven/css/components/form.css
+++ b/core/themes/seven/css/components/form.css
@@ -72,16 +72,12 @@ label[for] {
 .form-item input.error,
 .form-item textarea.error,
 .form-item select.error {
-  border-width: 1px;
+  border-width: 2px;
   border-color: #e62600;
   background-color: hsla(15, 75%, 97%, 1);
   box-shadow: inset 0 5px 5px -5px #b8b8b8;
   color: #a51b00;
 }
-.form-item textarea.error + .cke {
-  border-width: 1px;
-  border-color: #e62600;
-}
 .form-item input.error:focus,
 .form-item textarea.error:focus,
 .form-item select.error:focus {
@@ -96,19 +92,6 @@ label[for] {
   width: 7px;
   height: 7px;
 }
-.form-item--error-message {
-  margin-top: 0.15em;
-  color: #e32700;
-}
-.fieldset-wrapper > .form-item--error-message {
-  margin-top: 0;
-}
-.text-format-wrapper .form-item--error-message {
-  border: solid #ccc;
-  border-width: 0 1px;
-  margin: 0;
-  padding: 0.25em 0.666em 0;
-}
 
 
 /* Filter */
@@ -120,7 +103,6 @@ div.description,
   font-size: 0.95em;
 }
 .form-item .description.error {
-  margin-top: 0;
   color: #a51b00;
 }
 
@@ -199,6 +181,15 @@ textarea.form-textarea {
   width: auto;
 }
 
+.form-item .password-suggestions {
+  float: left; /* LTR */
+  clear: left; /* LTR */
+  width: 100%;
+}
+[dir="rtl"] .form-item .password-suggestions {
+  float: right;
+  clear: right;
+}
 .form-item-pass .description {
   clear: both;
 }
