diff --git a/src/Plugin/views/query/SearchApiQuery.php b/src/Plugin/views/query/SearchApiQuery.php
index c0d8851a..76032d76 100644
--- a/src/Plugin/views/query/SearchApiQuery.php
+++ b/src/Plugin/views/query/SearchApiQuery.php
@@ -323,6 +323,15 @@ public function getEntityTypes($return_bool = FALSE) {
   public function build(ViewExecutable $view) {
     $this->view = $view;
 
+    // Initialize the pager and let it modify the query to add limits. This has
+    // to be done even for aborted queries since it might otherwise lead to a
+    // fatal error when Views tries to access $view->pager.
+    $view->initPager();
+    $view->pager->query();
+
+    // If the query was aborted by some plugin (or, possibly, hook), we don't
+    // need to do anything else here. Adding conditions or other options to an
+    // aborted query doesn't make sense.
     if ($this->shouldAbort()) {
       return;
     }
@@ -365,10 +374,6 @@ public function build(ViewExecutable $view) {
       }
     }
 
-    // Initialize the pager and let it modify the query to add limits.
-    $view->initPager();
-    $view->pager->query();
-
     // Add the "search_api_bypass_access" option to the query, if desired.
     if (!empty($this->options['bypass_access'])) {
       $this->query->setOption('search_api_bypass_access', TRUE);
@@ -487,7 +492,7 @@ public function abort($msg = NULL) {
    * @see SearchApiQuery::abort()
    */
   public function shouldAbort() {
-    return $this->abort;
+    return $this->abort || !$this->query || $this->query->wasAborted();
   }
 
   /**
diff --git a/tests/src/Functional/ViewsTest.php b/tests/src/Functional/ViewsTest.php
index 7da8f196..359407b0 100644
--- a/tests/src/Functional/ViewsTest.php
+++ b/tests/src/Functional/ViewsTest.php
@@ -341,13 +341,17 @@ protected function regressionTests() {
   /**
    * Tests setting the "Fulltext search" filter to "Required".
    *
+   * This previously caused problems with form validation and caching.
+   *
    * @see https://www.drupal.org/node/2869121
+   * @see https://www.drupal.org/node/2873246
    */
   protected function regressionTest2869121() {
     // Make sure setting the fulltext filter to "Required" works as expected.
     $view = View::load('search_api_test_view');
     $displays = $view->get('display');
     $displays['default']['display_options']['filters']['search_api_fulltext']['expose']['required'] = TRUE;
+    $displays['default']['display_options']['cache']['type'] = 'search_api_time';
     $view->set('display', $displays);
     $view->save();
 
@@ -383,10 +387,14 @@ protected function regressionTest2869121() {
     ])->save();
 
     $this->drupalGet('');
-    $this->submitForm([], 'Search');
-    $this->assertSession()->addressEquals('search-api-test');
-    $this->assertSession()->responseNotContains('Error message');
-    $this->assertSession()->pageTextNotContains('search results');
+    // We submit the form three times, to make extra sure all Views caches are
+    // triggered.
+    for ($i = 0; $i < 3; ++$i) {
+      $this->submitForm([], 'Search');
+      $this->assertSession()->addressEquals('search-api-test');
+      $this->assertSession()->responseNotContains('Error message');
+      $this->assertSession()->pageTextNotContains('search results');
+    }
   }
 
   /**
