# Can a child workflow/activity be triggered from a Workflow CancellationScope in the exception block?

**URL:** <https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749>\
**Category:** Community Support\
**Tags:** java-sdk\
**Created:** [March 17, 2021, 4:51pm UTC](https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749 "2021-03-17T16:51:01Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![anjo](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/anjo/32/806_2.png) [@anjo](https://community.temporal.io/u/anjo)\
**Post date:** [March 17, 2021, 4:51pm UTC](https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749/1 "2021-03-17T16:51:01Z")

</div>

I have a workflow with CancellationScope. Can a child workflow/activity be triggered from CancellationScope in the exception block?

Are there any restrictions?

Below is an example where I call an activity method in the exception block?

```java
            try {
                cancelScope.run();
            } catch (Exception e) {
                cancelScope.cancel();
                helloActivity.cancelStuff();
            }

```

---

<div class="post-metadata">

**Author:** ![maxim](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/maxim/32/8_2.png) [@maxim](https://community.temporal.io/u/maxim)\
**Post date:** [March 17, 2021, 5:17pm UTC](https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749/2 "2021-03-17T17:17:29Z")

</div>

Your sample should run without any problem as `helloActivity.cancelStuff()` is executed outside of the scope that was canceled.

A trickier use case is when you need to run cleanup logic from within a canceled scope. Then you need to run a child workflow in a detached scope:

```java
             CancellationScope cancelScope = Workflow.newCancellationScope(
                      (scope) -> {
                                  try {
                                    Workflow.sleep(Duration.ofHours(1));
                                  } finally {
                                    Workflow.newDetachedCancellationScope(
                                       () -> { 
                                          helloActivity.cancelStuff();
                                       });
                                  }
                                 });

```

---

<div class="post-metadata">

**Author:** ![anjo](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/anjo/32/806_2.png) [@anjo](https://community.temporal.io/u/anjo)\
**Post date:** [March 17, 2021, 5:26pm UTC](https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749/3 "2021-03-17T17:26:19Z")

</div>

> [@maxim](#):
>
> ```auto
> Workflow.newDetachedCancellationScope(
> () -> { 
> helloActivity.cancelStuff();
> });
> 
> ```

Thanks

Yes, in fact i am trying to run clean up logic via a child workflow.

So triggering a child workflow as below should work with detached scope?

```
                 try {
                            cancelScope.run();
                        } catch (Exception e) {
                            cancelScope.cancel();

                            CancellationScope detached = Workflow.newDetachedCancellationScope(
                                    () -> {
                                        Promise<Void> promise = procedure(
                                                Workflow.newChildWorkflowStub(
                                                        ChildWorkflow.class, options)::doStuff, arg1);
                                        promise.get();
                                    });
                                  
                            detached.run();
                        }

```

---

<div class="post-metadata">

**Author:** ![maxim](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/maxim/32/8_2.png) [@maxim](https://community.temporal.io/u/maxim)\
**Post date:** [March 17, 2021, 6:09pm UTC](https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749/4 "2021-03-17T18:09:50Z")

</div>

You don’t need the detached scope in this case as the exception handler is not part of the scope that was canceled.

---

<div class="post-metadata">

**Author:** ![anjo](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/anjo/32/806_2.png) [@anjo](https://community.temporal.io/u/anjo)\
**Post date:** [March 17, 2021, 6:22pm UTC](https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749/5 "2021-03-17T18:22:56Z")

</div>

Sorry I am confused as to when to use **newDetachedCancellationScope**.

Your comments

> A trickier use case is when you need to run cleanup logic from within a canceled scope. Then you need to run a child workflow in a detached scope:

Would this be a right approach?

```
CancellationScope cancelScope = Workflow.newCancellationScope(() -> {
                // do stuff
            });
            try {
                cancelScope.run();
            } catch (Exception e) {
                cancelScope.cancel();
                CancellationScope detached = Workflow.newDetachedCancellationScope(
                        () -> {
                            //clean up logic
                            Promise<Void> promise = procedure(
                                    Workflow.newChildWorkflowStub(
                                            ChildWF.class, options)::doCleanUpStuff, arg1);
                            promise.get();
                        });
                detached.run();
            }

```

---

<div class="post-metadata">

**Author:** ![maxim](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/maxim/32/8_2.png) [@maxim](https://community.temporal.io/u/maxim)\
**Post date:** [March 17, 2021, 7:13pm UTC](https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749/6 "2021-03-17T19:13:27Z")

</div>

You need to use detached cancellation scope for the code that runs within cancelScope which is where your comment says “do stuff”. The `cancelScope.cancel` only cancels that code. The catch statement is outside of that scope, so there is no need to use the detached scope.

---

<div class="post-metadata">

**Author:** ![anjo](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/anjo/32/806_2.png) [@anjo](https://community.temporal.io/u/anjo)\
**Post date:** [March 18, 2021, 12:03pm UTC](https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749/7 "2021-03-18T12:03:55Z")

</div>

Thank you for clarifying

---

<div class="post-metadata">

**Author:** ![anjo](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/anjo/32/806_2.png) [@anjo](https://community.temporal.io/u/anjo)\
**Post date:** [March 18, 2021, 6:35pm UTC](https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749/8 "2021-03-18T18:35:20Z")

</div>

I do get this error when i call a Workflow in the exception block and the parent workflow is cancelled

> execute called from a canceled scope

```
try {
          cancelScope.run();
      } catch (Exception e) {

          cancelScope.cancel();

          // clean up logic

          Promise<Void> promise =
              procedure(
                  Workflow.newChildWorkflowStub(CleanupWorkflow.class, options)::cleanUp,
                  signals);
          promise.get();

      }

```

It works fine if I wrap the child workflow call in newDetachedCancellationScope

```
 try {
      cancelScope.run();
    } catch (Exception e) {
      cancelScope.cancel();
      CancellationScope detached =
          Workflow.newDetachedCancellationScope(
              () -> {
                // clean up logic
                Promise<Void> promise =
                    procedure(
                        Workflow.newChildWorkflowStub(CleanupWorkflow.class, options)::cleanUp,
                        signals);
                promise.get();
              });
      detached.run();
    }

```

---

<div class="post-metadata">

**Author:** ![anjo](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/anjo/32/806_2.png) [@anjo](https://community.temporal.io/u/anjo)\
**Post date:** [March 18, 2021, 6:52pm UTC](https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749/9 "2021-03-18T18:52:56Z")

</div>

Here is the complete sample - [samples-java/HelloSignalWorkflowImpl.java at SignalWF\_Test · ansujohn/samples-java · GitHub](https://github.com/ansujohn/samples-java/blob/SignalWF_Test/src/main/java/io/temporal/samples/helloSignal/HelloSignalWorkflowImpl.java)

---

<div class="post-metadata">

**Author:** ![maxim](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/maxim/32/8_2.png) [@maxim](https://community.temporal.io/u/maxim)\
**Post date:** [March 18, 2021, 9:38pm UTC](https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749/10 "2021-03-18T21:38:22Z")

</div>

CancellationScopes are hierarchical. If you cancel a parent scope all child scopes are canceled. If a child scope is canceled directly the parent scope is not affected.

In the original example you canceled the child scope explicitly through `cancelScope.cancel()` command. This didn’t affect the parent scope that contained the try-catch statement. So the detached cancellation scope wasn’t needed.

In the last example, you canceled the whole workflow which canceled the root workflow scope that wraps the main workflow method. As in this case the scope that contains try-catch is canceled the detached cancellation scope is needed.

---

<div class="post-metadata">

**Author:** ![anjo](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/anjo/32/806_2.png) [@anjo](https://community.temporal.io/u/anjo)\
**Post date:** [March 19, 2021, 10:08am UTC](https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749/11 "2021-03-19T10:08:43Z")

</div>

Thanku again.

I am trying to unit test the same scenario, cancel scenario with a mock for the CleanupWorkflow.

I get the error

> java.lang.IllegalStateException: Operation allowed only while eventLoop is running

Sample code is here :

> <https://github.com/ansujohn/samples-java/blob/SignalWF_Test/src/test/java/io/temporal/samples/helloSignal/HelloSignalWorkflowTest.java#L85>

I have used mocking workflow example from here - [https://github.com/temporalio/samples-java/blob/986f4965ca9799af36784f80e28a7a04c0e6f9eb/src/test/java/io/temporal/samples/hello/HelloChildTest.java](https://github.com/temporalio/samples-java/blob/986f4965ca9799af36784f80e28a7a04c0e6f9eb/src/test/java/io/temporal/samples/hello/HelloChildTest.java)

Also get this error Argument passed to verify() should be a mock but is null!

> <https://github.com/ansujohn/samples-java/blob/SignalWF_Test/src/test/java/io/temporal/samples/helloSignal/HelloSignalWorkflowTest.java#L124>

---

<div class="post-metadata">

**Author:** ![maxim](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/maxim/32/8_2.png) [@maxim](https://community.temporal.io/u/maxim)\
**Post date:** [March 19, 2021, 3:51pm UTC](https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749/12 "2021-03-19T15:51:55Z")

</div>

Cancellation is asynchronous. On line 121 you request cancellation and don’t wait for it to be processed before checking.

---

<div class="post-metadata">

**Author:** ![anjo](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/anjo/32/806_2.png) [@anjo](https://community.temporal.io/u/anjo)\
**Post date:** [March 19, 2021, 4:13pm UTC](https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749/13 "2021-03-19T16:13:56Z")

</div>

Thanks  
Adding testEnv.sleep worked, though as you pointed out in another thread, it doesn’t skip and hence increases the test run time.

---

<div class="post-metadata">

**Author:** ![maxim](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/maxim/32/8_2.png) [@maxim](https://community.temporal.io/u/maxim)\
**Post date:** [March 19, 2021, 4:50pm UTC](https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749/14 "2021-03-19T16:50:38Z")

</div>

You can wait for the workflow to complete instead of sleeping after canceling it.

---

<div class="post-metadata">

**Author:** ![anjo](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/anjo/32/806_2.png) [@anjo](https://community.temporal.io/u/anjo)\
**Post date:** [March 19, 2021, 5:02pm UTC](https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749/15 "2021-03-19T17:02:54Z")

</div>

Okay will try that.

Test using a mocked child workflow has issues.

> <https://github.com/ansujohn/samples-java/blob/SignalWF_Test/src/test/java/io/temporal/samples/helloSignal/HelloSignalWorkflowTest.java#L125>

Gives the error

> Argument passed to verify() should be a mock but is null!

---

<div class="post-metadata">

**Author:** ![maxim](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/maxim/32/8_2.png) [@maxim](https://community.temporal.io/u/maxim)\
**Post date:** [March 19, 2021, 5:07pm UTC](https://community.temporal.io/t/can-a-child-workflow-activity-be-triggered-from-a-workflow-cancellationscope-in-the-exception-block/1749/16 "2021-03-19T17:07:04Z")

</div>

I’m not sure why this happens. May be the way factory is implemented affects the result?
