diff --git a/lib/wp/session-item.c b/lib/wp/session-item.c
index d23c806e5dfaced0e04420653688f985c68ffad1..f2e2d6d1519c76a769ea06a7d89f60ecebc1669a 100644
--- a/lib/wp/session-item.c
+++ b/lib/wp/session-item.c
@@ -125,6 +125,21 @@ wp_session_item_default_get_next_step (WpSessionItem * self,
   return WP_TRANSITION_STEP_NONE;
 }
 
+static void
+wp_session_item_default_execute_step (WpSessionItem * self,
+    WpTransition * transition, guint step)
+{
+  switch (step) {
+  case WP_TRANSITION_STEP_NONE:
+    break;
+  case WP_TRANSITION_STEP_ERROR:
+    wp_session_item_reset (self);
+    break;
+  default:
+    g_return_if_reached ();
+  }
+}
+
 static void
 wp_session_item_default_reset (WpSessionItem * self)
 {
@@ -222,9 +237,9 @@ wp_session_item_class_init (WpSessionItemClass * klass)
   object_class->dispose = wp_session_item_dispose;
   object_class->finalize = wp_session_item_finalize;
 
-  klass->reset = wp_session_item_default_reset;
-
   klass->get_next_step = wp_session_item_default_get_next_step;
+  klass->execute_step = wp_session_item_default_execute_step;
+  klass->reset = wp_session_item_default_reset;
   klass->export = wp_session_item_default_export;
   klass->export_finish = wp_session_item_default_export_finish;
   klass->unexport = wp_session_item_default_unexport;
diff --git a/lib/wp/transition.c b/lib/wp/transition.c
index 24311893b3a9e1d5bca14eb8bbc333c9f0da9d21..03e8d15c8c3f940042cf8bead84bbf54a865ef27 100644
--- a/lib/wp/transition.c
+++ b/lib/wp/transition.c
@@ -367,6 +367,10 @@ wp_transition_return (WpTransition * self, WpTransitionPrivate *priv)
  * When #WpTransitionClass.get_next_step() returns %WP_TRANSITION_STEP_ERROR,
  * this function calls wp_transition_return_error(), unless it has already been
  * called directly by #WpTransitionClass.get_next_step().
+ *
+ * In error conditions, #WpTransitionClass.execute_step() is called once with
+ * @step being %WP_TRANSITION_STEP_ERROR, allowing the implementation to
+ * rollback any changes or cancel underlying jobs, if necessary.
  */
 void
 wp_transition_advance (WpTransition * self)
@@ -434,6 +438,11 @@ wp_transition_return_error (WpTransition * self, GError * error)
 
   priv->step = WP_TRANSITION_STEP_ERROR;
   priv->error = error;
+
+  /* allow the implementation to rollback changes */
+  if (WP_TRANSITION_GET_CLASS (self)->execute_step)
+    WP_TRANSITION_GET_CLASS (self)->execute_step (self, priv->step);
+
   wp_transition_return (self, priv);
 }
 
diff --git a/modules/module-si-adapter.c b/modules/module-si-adapter.c
index 221f26014a51ab90256e1dcb9668118da1fb77e3..dc3698d1ac3c46aed548b2d4e58ff297e4d435e3 100644
--- a/modules/module-si-adapter.c
+++ b/modules/module-si-adapter.c
@@ -341,7 +341,9 @@ si_adapter_execute_step (WpSessionItem * item, WpTransition * transition,
       break;
     }
     default:
-      g_return_if_reached ();
+      WP_SESSION_ITEM_GET_CLASS (si_adapter_parent_class)->execute_step (item,
+          transition, step);
+      break;
   }
 }
 
diff --git a/tests/wp/transition.c b/tests/wp/transition.c
index 5b173641c2dccb30e33eca1e0ad01327979b0d6d..949cb6369eec4a6871d1e96f5ad6a786a0b10a8d 100644
--- a/tests/wp/transition.c
+++ b/tests/wp/transition.c
@@ -102,8 +102,10 @@ wp_test_transition_execute_step (WpTransition * transition, guint step)
   WpTestTransition * self = WP_TEST_TRANSITION (transition);
   struct data *d = wp_transition_get_data (transition);
 
-  g_assert_cmpint (step, >=, STEP_FIRST);
-  g_assert_cmpint (step, <=, STEP_FINISH);
+  if (step != WP_TRANSITION_STEP_ERROR) {
+    g_assert_cmpint (step, >=, STEP_FIRST);
+    g_assert_cmpint (step, <=, STEP_FINISH);
+  }
 
   g_assert_nonnull (d);
   g_assert_cmpint (d->ste_i, <, 10);
@@ -115,7 +117,8 @@ wp_test_transition_execute_step (WpTransition * transition, guint step)
     return;
   }
 
-  g_idle_add (advance_on_idle, transition);
+  if (step != WP_TRANSITION_STEP_ERROR)
+    g_idle_add (advance_on_idle, transition);
 }
 
 static void
@@ -260,7 +263,8 @@ test_transition_error (void)
   g_assert_cmpint (data.ste[0], ==, STEP_FIRST);
   g_assert_cmpint (data.ste[1], ==, STEP_SECOND);
   g_assert_cmpint (data.ste[2], ==, STEP_THIRD);
-  g_assert_cmpint (data.ste_i, ==, 3);
+  g_assert_cmpint (data.ste[3], ==, WP_TRANSITION_STEP_ERROR);
+  g_assert_cmpint (data.ste_i, ==, 4);
   g_assert_true (data.destroyed);
 }