diff --git a/modules/salesforce_example/src/EventSubscriber/SalesforceExampleSubscriber.php b/modules/salesforce_example/src/EventSubscriber/SalesforceExampleSubscriber.php
index 6f8f65090a5da08a0ccec9655930c24320c22fd8..96536fcf85e2b88058a744dbe117df3374da5876 100644
--- a/modules/salesforce_example/src/EventSubscriber/SalesforceExampleSubscriber.php
+++ b/modules/salesforce_example/src/EventSubscriber/SalesforceExampleSubscriber.php
@@ -4,6 +4,7 @@ namespace Drupal\salesforce_example\EventSubscriber;
 
 use Drupal\salesforce\Event\SalesforceEvents;
 use Drupal\salesforce_mapping\Event\SalesforcePushOpEvent;
+use Drupal\salesforce_mapping\Event\SalesforcePushAllowedEvent;
 use Drupal\salesforce_mapping\Event\SalesforcePushParamsEvent;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Drupal\salesforce\Exception;
@@ -20,11 +21,11 @@ class SalesforceExampleSubscriber implements EventSubscriberInterface {
   /**
    *
    */
-  public function pushAllowed(SalesforcePushOpEvent $event) {
+  public function pushAllowed(SalesforcePushAllowedEvent $event) {
     /** @var \Drupal\Core\Entity\Entity $entity */
     $entity = $event->getEntity();
     if ($entity && $entity->getEntityTypeId() == 'unpushable_entity') {
-      throw new Exception('Prevent push of Unpushable Entity');
+      $event->disallowPush();
     }
   }
 
diff --git a/modules/salesforce_mapping/src/Event/SalesforcePushAllowedEvent.php b/modules/salesforce_mapping/src/Event/SalesforcePushAllowedEvent.php
new file mode 100644
index 0000000000000000000000000000000000000000..fd6a4774574054606cefe9386c9894b32e61812c
--- /dev/null
+++ b/modules/salesforce_mapping/src/Event/SalesforcePushAllowedEvent.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace Drupal\salesforce_mapping\Event;
+
+use Drupal\salesforce_mapping\Entity\MappedObjectInterface;
+
+/**
+ *
+ */
+class SalesforcePushAllowedEvent extends SalesforcePushOpEvent {
+
+  protected $op;
+  protected $push_allowed;
+
+  /**
+   * {@inheritdoc}
+   *
+   * SalesforcePushAllowedEvent is fired when PushParams are not available, for
+   * example on SalesforceEvents::PUSH_ALLOWED, before any entities have been
+   * loaded.
+   *
+   * @param \Drupal\salesforce_mapping\Entity\MappedObjectInterface $mapped_object
+   * @param string $op
+   *   One of
+   *     Drupal\salesforce_mapping\MappingConstants::
+   *       SALESFORCE_MAPPING_SYNC_DRUPAL_CREATE
+   *       SALESFORCE_MAPPING_SYNC_DRUPAL_UPDATE
+   *       SALESFORCE_MAPPING_SYNC_DRUPAL_DELETE.
+   */
+  public function __construct(MappedObjectInterface $mapped_object, $op) {
+    parent::__construct($mapped_object);
+    $this->op = $op;
+  }
+
+  /**
+   *
+   */
+  public function getOp() {
+    return $this->op;
+  }
+
+  /**
+   * Returns FALSE if PUSH_ALLOWED event has been fired, and any subscriber
+   * wants to prevent push. Otherwise, returns NULL. Note: a subscriber cannot
+   * "force" a push when any other subscriber which wants to prevent pushing.
+   *
+   * @return FALSE or NULL
+   */
+  public function isPushAllowed() {
+    return $this->push_allowed === FALSE ? FALSE : NULL;
+  }
+
+  /**
+   * Stop Salesforce record from being pushed.
+   *
+   * @return $this
+   */
+  public function disallowPush() {
+    $this->push_allowed = FALSE;
+    return $this;
+  }
+
+}
diff --git a/modules/salesforce_pull/src/Plugin/QueueWorker/PullBase.php b/modules/salesforce_pull/src/Plugin/QueueWorker/PullBase.php
index 5ef06579c3320fd84cc8470934de1e92164de144..dabfdc1d4682cca1d7badffaebf494c94882a2db 100644
--- a/modules/salesforce_pull/src/Plugin/QueueWorker/PullBase.php
+++ b/modules/salesforce_pull/src/Plugin/QueueWorker/PullBase.php
@@ -196,12 +196,9 @@ abstract class PullBase extends QueueWorkerBase implements ContainerFactoryPlugi
       }
     }
     catch (\Exception $e) {
-      $this->eventDispatcher->dispatch(SalesforceEvents::ERROR, new SalesforceErrorEvent($e, 'Failed to update entity %label from Salesforce object %sfobjectid.', ['%label' => (isset($entity)) ? $entity->label() : "Unknown", '%sfobjectid' => (string) $sf_object->id()]));
-
-      if ($e instanceof PullException) {
-        // Throwing a new exception to keep current item in queue in Cron.
-        throw $e;
-      }
+      $this->eventDispatcher->dispatch(SalesforceEvents::WARNING, new SalesforceErrorEvent($e, 'Failed to update entity %label from Salesforce object %sfobjectid.', ['%label' => (isset($entity)) ? $entity->label() : "Unknown", '%sfobjectid' => (string) $sf_object->id()]));
+      // Throwing a new exception keeps current item in cron queue.
+      throw $e;
     }
   }
 
@@ -284,11 +281,9 @@ abstract class PullBase extends QueueWorkerBase implements ContainerFactoryPlugi
       return MappingConstants::SALESFORCE_MAPPING_SYNC_SF_CREATE;
     }
     catch (\Exception $e) {
-      $this->eventDispatcher->dispatch(SalesforceEvents::NOTICE, new SalesforceNoticeEvent($e, 'Pull-create failed for Salesforce Object ID: %sfobjectid', ['%sfobjectid' => (string) $sf_object->id()]));
-      if ($e instanceof PullException) {
-        // Throwing a new exception to keep current item in queue in Cron.
-        throw $e;
-      }
+      $this->eventDispatcher->dispatch(SalesforceEvents::WARNING, new SalesforceNoticeEvent($e, 'Pull-create failed for Salesforce Object ID: %sfobjectid', ['%sfobjectid' => (string) $sf_object->id()]));
+      // Throwing a new exception to keep current item in cron queue.
+      throw $e;
     }
   }
 
diff --git a/modules/salesforce_push/salesforce_push.module b/modules/salesforce_push/salesforce_push.module
index a62ac73c8a1bdff4774473ebf28467d7327e5e99..c8115e41dcea8dd8579e88d74ae81c46978b6bcf 100644
--- a/modules/salesforce_push/salesforce_push.module
+++ b/modules/salesforce_push/salesforce_push.module
@@ -122,11 +122,14 @@ function salesforce_push_entity_crud_mapping(EntityInterface $entity, $op, Sales
     $mapped_object = current($mapped_objects);
   }
 
-  // Event subscribers should throw an exception to prevent push.
-  \Drupal::service('event_dispatcher')->dispatch(
+  // Event subscribers should call $event->disallowPush() to prevent push.
+  $event = \Drupal::service('event_dispatcher')->dispatch(
     SalesforceEvents::PUSH_ALLOWED,
-    new SalesforcePushOpEvent($mapped_object, $op)
+    new SalesforcePushAllowedEvent($mapped_object, $op)
   );
+  if ($event->isPushAllowed() === FALSE) {
+    return FALSE;
+  }
 
   // Enqueue async push if the mapping is configured to do so, and quit.
   if ($mapping->async) {
@@ -141,7 +144,6 @@ function salesforce_push_entity_crud_mapping(EntityInterface $entity, $op, Sales
 
   // Attempt real-time push. Enqueue async push on failure.
   try {
-    // @TODO this doesn't really seem distinct from PUSH_ALLOWED anymore. Do we still have a use case for this event?
     \Drupal::service('event_dispatcher')->dispatch(
       SalesforceEvents::PUSH_MAPPING_OBJECT,
       new SalesforcePushOpEvent($mapped_object, $op)
diff --git a/modules/salesforce_push/src/PushQueue.php b/modules/salesforce_push/src/PushQueue.php
index f59272338f96b2712ac4604854df11d65dfe11c8..d732a35b88b971e4d9f3df8694209f3c2adaf4c6 100644
--- a/modules/salesforce_push/src/PushQueue.php
+++ b/modules/salesforce_push/src/PushQueue.php
@@ -388,7 +388,7 @@ class PushQueue extends DatabaseQueue implements PushQueueInterface {
         // Getting a Requeue here is weird for a group of items, but we'll
         // deal with it.
         $this->releaseItems($items);
-        $this->eventDispatcher->dispatch(SalesforceEvents::ERROR, new SalesforceErrorEvent($e));
+        $this->eventDispatcher->dispatch(SalesforceEvents::WARNING, new SalesforceErrorEvent($e));
         continue;
       }
       catch (SuspendQueueException $e) {
@@ -396,7 +396,7 @@ class PushQueue extends DatabaseQueue implements PushQueueInterface {
         // or authorization error. Release items and move on to the next
         // mapping in this case.
         $this->releaseItems($items);
-        $this->eventDispatcher->dispatch(SalesforceEvents::ERROR, new SalesforceErrorEvent($e));
+        $this->eventDispatcher->dispatch(SalesforceEvents::WARNING, new SalesforceErrorEvent($e));
         return $i;
       }
       catch (\Exception $e) {
diff --git a/src/Event/SalesforceEvents.php b/src/Event/SalesforceEvents.php
index c223ad9000d4263e11c24bd8d2df016aae26cda6..00fd243c4e084369d3b05cad87550a23c1b75be1 100644
--- a/src/Event/SalesforceEvents.php
+++ b/src/Event/SalesforceEvents.php
@@ -14,7 +14,7 @@ class SalesforceEvents {
    * The event listener method receives a
    * \Drupal\salesforce_mapping\Event\SalesforcePushOpEvent instance.
    *
-   * Event listeners should throw an exception to prevent push.
+   * Event listeners should call $event->disallowPush() to prevent push.
    *
    * @Event
    *
@@ -92,11 +92,15 @@ class SalesforceEvents {
    * example, to alter SF object retrieved from Salesforce or to assign a
    * different Drupal entity.
    *
-   * Listeners should throw an exception to prevent an item from being pulled.
+   * Listeners should throw an exception to prevent an item from being pulled,
+   * per Drupal\Core\Queue\QueueWorkerInterface
+   *
+   * @see \Drupal\Core\Queue\QueueWorkerInterface
    *
    * @Event
    *
    * @var string
+   *
    */
   const PULL_PREPULL = 'salesforce.pull_prepull';