diff --git a/core/lib/Drupal/Core/Form/FormValidator.php b/core/lib/Drupal/Core/Form/FormValidator.php
index dbefe00a846d1b96a532a5300626cd90797e38ca..d4f3ee564014182499ac7e06c125fe32c281d754 100644
--- a/core/lib/Drupal/Core/Form/FormValidator.php
+++ b/core/lib/Drupal/Core/Form/FormValidator.php
@@ -229,8 +229,12 @@ protected function finalizeValidation(&$form, FormStateInterface &$form_state, $
    *   theming, and hook_form_alter functions.
    */
   protected function doValidateForm(&$elements, FormStateInterface &$form_state, $form_id = NULL) {
-    // Recurse through all children.
-    foreach (Element::children($elements) as $key) {
+    // Recurse through all children, sorting the elements so that the order of
+    // error messages displayed to the user matches the order of elements in
+    // the form. Use a copy of $elements so that it is not modified by the
+    // sorting itself.
+    $elements_sorted = $elements;
+    foreach (Element::children($elements_sorted, TRUE) as $key) {
       if (isset($elements[$key]) && $elements[$key]) {
         $this->doValidateForm($elements[$key], $form_state);
       }
diff --git a/core/tests/Drupal/KernelTests/Core/Form/FormValidationMessageOrderTest.php b/core/tests/Drupal/KernelTests/Core/Form/FormValidationMessageOrderTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..33ff22b02e706c6b6b239b2d71f86740fc438660
--- /dev/null
+++ b/core/tests/Drupal/KernelTests/Core/Form/FormValidationMessageOrderTest.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace Drupal\KernelTests\Core\Form;
+
+use Drupal\Core\Form\FormInterface;
+use Drupal\Core\Form\FormState;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * Tests form validation mesages are displayed in the same order as the fields.
+ *
+ * @group Form
+ */
+class FormValidationMessageOrderTest extends KernelTestBase implements FormInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'form_validation_error_message_order_test';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    // Prepare fields with weights specified.
+    $form['one'] = [
+      '#type' => 'textfield',
+      '#title' => 'One',
+      '#required' => TRUE,
+      '#weight' => 40,
+    ];
+    $form['two'] = [
+      '#type' => 'textfield',
+      '#title' => 'Two',
+      '#required' => TRUE,
+      '#weight' => 30,
+    ];
+    $form['three'] = [
+      '#type' => 'textfield',
+      '#title' => 'Three',
+      '#required' => TRUE,
+      '#weight' => 10,
+    ];
+    $form['four'] = [
+      '#type' => 'textfield',
+      '#title' => 'Four',
+      '#required' => TRUE,
+      '#weight' => 20,
+    ];
+    $form['actions'] = [
+      '#type' => 'actions',
+      'submit' => [
+        '#type' => 'submit',
+        '#value' => 'Submit',
+      ],
+    ];
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+  }
+
+  /**
+   * Tests that fields validation messages are sorted in the fields order.
+   */
+  function testLimitValidationErrors() {
+    $form_state = new FormState();
+    $form_builder = $this->container->get('form_builder');
+    $form_builder->submitForm($this, $form_state);
+
+    $messages = drupal_get_messages();
+    $this->assertTrue(isset($messages['error']));
+    $error_messages = $messages['error'];
+    $this->assertEqual($error_messages[0], 'Three field is required.');
+    $this->assertEqual($error_messages[1], 'Four field is required.');
+    $this->assertEqual($error_messages[2], 'Two field is required.');
+    $this->assertEqual($error_messages[3], 'One field is required.');
+  }
+
+}