diff --git a/core/modules/big_pipe/js/big_pipe.es6.js b/core/modules/big_pipe/js/big_pipe.es6.js
index b78eaa9c708233f67e0b190775dabe17e805fbf7..df72915a8ed674a6e73a505839d929421ecc2c01 100644
--- a/core/modules/big_pipe/js/big_pipe.es6.js
+++ b/core/modules/big_pipe/js/big_pipe.es6.js
@@ -20,16 +20,17 @@
     // Ignore any placeholders that are not in the known placeholder list. Used
     // to avoid someone trying to XSS the site via the placeholdering mechanism.
     if (typeof drupalSettings.bigPipePlaceholderIds[placeholderId] !== 'undefined') {
+      const response = mapTextContentToAjaxResponse(content);
       // If we try to parse the content too early (when the JSON containing Ajax
-      // commands is still arriving), textContent will be empty which will cause
-      // JSON.parse() to fail. Remove once so that it can be processed again
-      // later.
-      // @see bigPipeProcessDocument()
-      if (content === '') {
+      // commands is still arriving), textContent will be empty or incomplete.
+      if (response === false) {
+        /**
+         * Mark as unprocessed so this will be retried later.
+         * @see bigPipeProcessDocument()
+         */
         $(this).removeOnce('big-pipe');
       }
       else {
-        const response = JSON.parse(content);
         // Create a Drupal.Ajax object without associating an element, a
         // progress indicator or a URL.
         const ajaxObject = Drupal.ajax({
@@ -45,6 +46,28 @@
     }
   }
 
+  /**
+   * Maps textContent of <script type="application/vnd.drupal-ajax"> to an AJAX response.
+   *
+   * @param {string} content
+   *   The text content of a <script type="application/vnd.drupal-ajax"> DOM node.
+   * @return {Array|boolean}
+   *   The parsed Ajax response containing an array of Ajax commands, or false in
+   *   case the DOM node hasn't fully arrived yet.
+   */
+  function mapTextContentToAjaxResponse(content) {
+    if (content === '') {
+      return false;
+    }
+
+    try {
+      return JSON.parse(content);
+    }
+    catch (e) {
+      return false;
+    }
+  }
+
   /**
    * Processes a streamed HTML document receiving placeholder replacements.
    *
diff --git a/core/modules/big_pipe/js/big_pipe.js b/core/modules/big_pipe/js/big_pipe.js
index 1464fdd968202268b4a23be8e1004e4d1152fc3f..b8a25f77cc8987832ec660ba87dea22329419b83 100644
--- a/core/modules/big_pipe/js/big_pipe.js
+++ b/core/modules/big_pipe/js/big_pipe.js
@@ -11,11 +11,11 @@
     var content = this.textContent.trim();
 
     if (typeof drupalSettings.bigPipePlaceholderIds[placeholderId] !== 'undefined') {
-      if (content === '') {
+      var response = mapTextContentToAjaxResponse(content);
+
+      if (response === false) {
         $(this).removeOnce('big-pipe');
       } else {
-        var response = JSON.parse(content);
-
         var ajaxObject = Drupal.ajax({
           url: '',
           base: false,
@@ -28,6 +28,18 @@
     }
   }
 
+  function mapTextContentToAjaxResponse(content) {
+    if (content === '') {
+      return false;
+    }
+
+    try {
+      return JSON.parse(content);
+    } catch (e) {
+      return false;
+    }
+  }
+
   function bigPipeProcessDocument(context) {
     if (!context.querySelector('script[data-big-pipe-event="start"]')) {
       return false;