# Best practice for handling lost external events and resuming paused workflows

**URL:** <https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647>\
**Category:** Community Support\
**Tags:** java-sdk, polling\
**Created:** [November 10, 2025, 8:04am UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647 "2025-11-10T08:04:40Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Abhinav\_Verma](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/abhinav_verma/32/4887_2.png) [@Abhinav\_Verma](https://community.temporal.io/u/Abhinav_Verma)\
**Post date:** [November 10, 2025, 8:04am UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/1 "2025-11-10T08:04:41Z")

</div>

Hi Temporal Community,

I have a workflow that pauses while waiting for an asynchronous downstream operation to complete. The downstream system publishes an event to Kafka once the operation is done, and my service listens to this event and triggers a webhook to send a signal to resume the Temporal workflow.

However, there are cases where these downstream events might be **lost or not delivered** , causing the workflow to remain **paused indefinitely** and eventually **time out**.

To handle this, I’m exploring the idea of adding a **polling fallback mechanism** — so that when the workflow is reset or retried, it can poll the downstream system to verify if the operation has already completed and then resume accordingly.

I wanted to check:

- Does Temporal provide any **built-in support or recommended pattern** for handling such scenarios where external signals might be lost?

- If not, what would be the **best practice** to implement this kind of recovery or polling mechanism within a Temporal workflow?

Thanks in advance for your insights!

---

<div class="post-metadata">

**Author:** ![Abhinav\_Verma](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/abhinav_verma/32/4887_2.png) [@Abhinav\_Verma](https://community.temporal.io/u/Abhinav_Verma)\
**Post date:** [November 10, 2025, 11:01am UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/2 "2025-11-10T11:01:41Z")

</div>

@maxim Also my polling frequency will be after every 1 hour.

---

<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:** [November 10, 2025, 3:11pm UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/3 "2025-11-10T15:11:50Z")

</div>

> [@What is the best practice for a polling activity?](https://community.temporal.io/t/what-is-the-best-practice-for-a-polling-activity/328/2):
>
> It depends on how frequently you want to poll. For infrequent polls (every minute or slower) use the server side retry. Specify a [RetryPolicy](https://github.com/uber-go/cadence-client/blob/c3cd9f8f9745172572659a28ad1b478c19596def/internal/activity.go#L107) (or [RetryOptions](https://github.com/uber/cadence-java-client/blob/1b2a2ce0de2bc92a609080186009dd793920b46c/src/main/java/com/uber/cadence/activity/ActivityOptions.java#L141) for Java) when invoking the activity. In the RetryPolicy specify an exponential coefficient of 1 and an initial interval of the poll frequency. Then fail the activity in case the polled resource is not ready and the server is going to retry it up to the specified retry policy expiration interval. So what you are doing is absolutely OK. …

> **[samples-java/core/src/main/java/io/temporal/samples/polling at main ·...](https://github.com/temporalio/samples-java/tree/main/core/src/main/java/io/temporal/samples/polling)**
>
> Temporal Java SDK samples. Contribute to temporalio/samples-java development by creating an account on GitHub.

---

<div class="post-metadata">

**Author:** ![Abhinav\_Verma](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/abhinav_verma/32/4887_2.png) [@Abhinav\_Verma](https://community.temporal.io/u/Abhinav_Verma)\
**Post date:** [November 11, 2025, 3:14am UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/4 "2025-11-11T03:14:14Z")

</div>

@maxim I’m planning to implement periodic polling that runs every hour. My idea is to create a child workflow responsible for polling and notifying the parent workflow once it completes (as provided in samples-java repo), based on the downstream state. Do you see any potential concerns or drawbacks with this approach?

---

<div class="post-metadata">

**Author:** ![Abhinav\_Verma](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/abhinav_verma/32/4887_2.png) [@Abhinav\_Verma](https://community.temporal.io/u/Abhinav_Verma)\
**Post date:** [November 11, 2025, 7:44am UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/5 "2025-11-11T07:44:50Z")

</div>

@maxim I’m implementing a parent workflow that must do two things at once:

1. Kick off a child workflow that periodically polls some downstream state and returns `true` when the polling condition is met.

2. Listen for an external signal that can set a `workflowPaused` flag at any time.

Current code (simplified):

```auto
PollingChildWorkflow childWorkflow =
  Workflow.newChildWorkflowStub(PollingChildWorkflow.class,
    ChildWorkflowOptions.newBuilder().setWorkflowId("ChildWorkflowPoll").build());

boolean success = childWorkflow.exec(pollingIntervalInSeconds);

Workflow.await(() -> success || workflowPaused);

```

The issue: `childWorkflow.exec(...)` blocks the parent until the child completes, so the parent can’t concurrently react to signals while the child is polling.

Question : what are the recommended patterns in Temporal to run the polling child and wait for a signal in parallel? For example — should I invoke the child asynchronously, have the child send a signal back to the parent on completion, convert the poller into an activity, use promises/async APIs, or something else? Any example snippets, pitfalls, or best practices would be really helpful.

---

<div class="post-metadata">

**Author:** ![antonio.perez](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/antonio.perez/32/2417_2.png) [@antonio.perez](https://community.temporal.io/u/antonio.perez)\
**Post date:** [November 11, 2025, 2:07pm UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/6 "2025-11-11T14:07:25Z")

</div>

Hi,

This post shows how to start a child workflow async in Java, [Best way to create an async child workflow](https://community.temporal.io/t/best-way-to-create-an-async-child-workflow/114) , sample code [here](https://github.com/antmendoza/temporal_samples-java/blob/fd5bd1f1d53ff8bd8079fab0971dde1e9a1bcbdc/core/src/main/java/io/temporal/samples/asyncchild/ParentWorkflowImpl.java#L23-L30) .

Something like this should work

```auto
ChildWorkflow child = Workflow.newChildWorkflowStub(ChildWorkflow.class, childWorkflowOptions);
Promise<String> result = Async.function(child::executeChild);
result.thenApply(
    (String r) -> {
      done = true;
      return r;
    });

Workflow.await(() -> done || signal_received);

```

You can put the logic within a [cancellation scope](https://docs.temporal.io/develop/java/cancellation#cancellation-scopes) to cancel the timer if the signal arrives first.

Another approach can be start the child workflow after the workflow await times out,

```auto
Workflow.await(duration, () -> sinal_received);

//child workflow polling here

```

so you give time to kafka to send the signal back to the parent workflow

---

<div class="post-metadata">

**Author:** ![Abhinav\_Verma](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/abhinav_verma/32/4887_2.png) [@Abhinav\_Verma](https://community.temporal.io/u/Abhinav_Verma)\
**Post date:** [November 11, 2025, 2:47pm UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/7 "2025-11-11T14:47:29Z")

</div>

Thanks @antonio.perez . I’m implementing **periodic polling** as a **child workflow** , which will be triggered by the parent workflow.

In the official Temporal Java samples, the polling logic is implemented directly inside the workflow code. However, that is not a good practice [Polling in workflow vs. Activity? - #2 by maxim](https://community.temporal.io/t/polling-in-workflow-vs-activity/453/2).

Since in my design the polling runs inside a **child workflow** , and the **parent workflow only records the start and end events** , am I correct in assuming that this approach won’t negatively impact the parent workflow’s history size or performance?

---

<div class="post-metadata">

**Author:** ![antonio.perez](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/antonio.perez/32/2417_2.png) [@antonio.perez](https://community.temporal.io/u/antonio.perez)\
**Post date:** [November 11, 2025, 4:28pm UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/8 "2025-11-11T16:28:27Z")

</div>

Hi,

> In the official Temporal Java samples, the polling logic is implemented directly inside the workflow code

Could you show me where? note that [this](https://github.com/temporalio/samples-java/blob/main/core/src/main/java/io/temporal/samples/polling/infrequent/README.md) is relaying on activity retries , not calling an activity in a loop.

> I correct in assuming that this approach won’t negatively impact the parent workflow’s history size or performance?

it will add three event to the workflow history ( StartChildWorkflowExecutionInitiated/Failed.., ChildWorkflowExecutionStarted, ChildWorkflowExecutionCompleted/Failed/Cancelled)

---

<div class="post-metadata">

**Author:** ![Abhinav\_Verma](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/abhinav_verma/32/4887_2.png) [@Abhinav\_Verma](https://community.temporal.io/u/Abhinav_Verma)\
**Post date:** [November 11, 2025, 6:30pm UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/9 "2025-11-11T18:30:25Z")

</div>

@antonio.perez [samples-java/core/src/main/java/io/temporal/samples/polling/periodicsequence/PeriodicPollingChildWorkflowImpl.java at main · temporalio/samples-java · GitHub](https://github.com/temporalio/samples-java/blob/main/core/src/main/java/io/temporal/samples/polling/periodicsequence/PeriodicPollingChildWorkflowImpl.java) Here you can see we are calling activity for the number of attempts. Also let’s say If my parent workflow is running for 2-3 days will child workflow history impact if i’m using it for polling for every 1 hour ??

---

<div class="post-metadata">

**Author:** ![antonio.perez](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/antonio.perez/32/2417_2.png) [@antonio.perez](https://community.temporal.io/u/antonio.perez)\
**Post date:** [November 11, 2025, 6:43pm UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/10 "2025-11-11T18:43:42Z")

</div>

Thanks

> <https://github.com/temporalio/samples-java/blob/626bf032cd168ffd353305a7662c2e72f6bc0ce1/core/src/main/java/io/temporal/samples/polling/periodicsequence/README.md?plain=1#L5-L7>

> Also let’s say If my parent workflow is running for 2-3 days will child workflow history impact if i’m using it for polling for every 1 hour ??

Sorry, does this answer your question or is this a different one?

> it will add three event to the workflow history ( StartChildWorkflowExecutionInitiated/Failed.., ChildWorkflowExecutionStarted, ChildWorkflowExecutionCompleted/Failed/Cancelled)

I have the felling that you don’t need a child workflow, [infrequent polling](https://github.com/temporalio/samples-java/tree/626bf032cd168ffd353305a7662c2e72f6bc0ce1/core/src/main/java/io/temporal/samples/polling/infrequent) should do what you need

---

<div class="post-metadata">

**Author:** ![Abhinav\_Verma](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/abhinav_verma/32/4887_2.png) [@Abhinav\_Verma](https://community.temporal.io/u/Abhinav_Verma)\
**Post date:** [November 12, 2025, 2:54am UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/11 "2025-11-12T02:54:05Z")

</div>

@antonio.perez For infrequent polling, I’ll need to start a new workflow as well, right? I was thinking of using a child workflow for this.

Can this be achieved without creating a new workflow? Are you suggesting that I should directly call an activity from my parent workflow instead? This will IMO as well add events like ActivityTaskScheduled/Completed per retry.

---

<div class="post-metadata">

**Author:** ![Abhinav\_Verma](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/abhinav_verma/32/4887_2.png) [@Abhinav\_Verma](https://community.temporal.io/u/Abhinav_Verma)\
**Post date:** [November 12, 2025, 7:35am UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/12 "2025-11-12T07:35:34Z")

</div>

@maxim @antonio.perez

I have the following code snippet in my workflow:

Promise result = Async.function(pollHandlerActivity::poll, req);  
Workflow.await(() → isPaused || result.get());

As I understand it, the activity will continue executing asynchronously in the background (including any retries, as per its retry policy). Meanwhile, the workflow will remain paused at the `Workflow.await()` line until either the activity completes (`result.get() == true`) or the `isPaused` flag becomes `true`.

From my perspective, when the predicate will be evaluated since I have used result.get() it will block until the activity completes. Is there any better way to do this ??

---

<div class="post-metadata">

**Author:** ![awwx](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/awwx/32/8185_2.png) [@awwx](https://community.temporal.io/u/awwx)\
**Post date:** [November 12, 2025, 9:45am UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/13 "2025-11-12T09:45:45Z")

</div>

I think `result.get()` is not what you want here, because `get()` is a blocking call, it will wait until the activity has completed. I think what you want here is `result.isCompleted()`, which will tell you if the activity has completed or not.

---

<div class="post-metadata">

**Author:** ![Abhinav\_Verma](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/abhinav_verma/32/4887_2.png) [@Abhinav\_Verma](https://community.temporal.io/u/Abhinav_Verma)\
**Post date:** [November 12, 2025, 10:22am UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/14 "2025-11-12T10:22:13Z")

</div>

> <https://github.com/temporalio/samples-java/blob/626bf032cd168ffd353305a7662c2e72f6bc0ce1/core/src/main/java/io/temporal/samples/polling/infrequent/InfrequentPollingActivityImpl.java#L21>

I’m using temporal sdk - 1.23.1 and it don’t have newBuilder() in ApplicationFailure class. What is the alternative for this ??

Can I do something like

```auto
throw Activity.wrap(e);

```

---

<div class="post-metadata">

**Author:** ![Abhinav\_Verma](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/abhinav_verma/32/4887_2.png) [@Abhinav\_Verma](https://community.temporal.io/u/Abhinav_Verma)\
**Post date:** [November 12, 2025, 11:11am UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/15 "2025-11-12T11:11:28Z")

</div>

@awwx When I use `result.isCompleted()`, if it evaluates to `true`, that could also mean the activity completed **with an exception** , not just successfully. In that case, my `Workflow.await()` condition would still evaluate to `true`, even though the activity actually failed — which isn’t what I want. So this approach wouldn’t work correctly for my use case.

---

<div class="post-metadata">

**Author:** ![awwx](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/awwx/32/8185_2.png) [@awwx](https://community.temporal.io/u/awwx)\
**Post date:** [November 12, 2025, 11:34am UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/16 "2025-11-12T11:34:27Z")

</div>

To use automatic activity retries, you throw an exception from the activity if you check and the operation hasn’t completed yet. The activity won’t be retried if it completes normally, even if it returns false or something. [samples-java/core/src/main/java/io/temporal/samples/polling/TestService.java at 626bf032cd168ffd353305a7662c2e72f6bc0ce1 · temporalio/samples-java · GitHub](https://github.com/temporalio/samples-java/blob/626bf032cd168ffd353305a7662c2e72f6bc0ce1/core/src/main/java/io/temporal/samples/polling/TestService.java#L33)

Now the activity will only complete if the poll determines that the operation has completed, and the activity returns normally without throwing an exception.

If you don’t want to use infinite retries and so need to check whether the activity completed normally or with an exception, I think you’d want to do something like wait for isCompleted() and then check the result; I think just calling get() is going to block your await.

---

<div class="post-metadata">

**Author:** ![antonio.perez](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/antonio.perez/32/2417_2.png) [@antonio.perez](https://community.temporal.io/u/antonio.perez)\
**Post date:** [November 12, 2025, 11:59am UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/17 "2025-11-12T11:59:12Z")

</div>

> Workflow.await(() → isPaused || result.get());

I think this should work, the only problem I see is that `.get` will throw an exception is the activity fails (after all retries are exhausted) .

This is another approach

```auto
Promise<Boolean> result = Async.function(activity::execute);
        result.thenApply(
                (Boolean r) -> {
                    done = true;
                    return r;
                }).exceptionally((ex -> {

//your logic to handle the failure here
            done = false;
            return null;
        }));

    Workflow.await(() -> done || signal_received);

```

---

<div class="post-metadata">

**Author:** ![Abhinav\_Verma](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/abhinav_verma/32/4887_2.png) [@Abhinav\_Verma](https://community.temporal.io/u/Abhinav_Verma)\
**Post date:** [November 12, 2025, 1:12pm UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/18 "2025-11-12T13:12:43Z")

</div>

> [@antonio.perez](#):
>
> `result.thenApply`

Thanks @antonio.perez result.thenApply is non-blocking right ??

---

<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:** [November 17, 2025, 12:15am UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/19 "2025-11-17T00:15:16Z")

</div>

Right, it returns a Promise without blocking

---

<div class="post-metadata">

**Author:** ![Abhinav\_Verma](https://sea2.discourse-cdn.com/flex016/user_avatar/community.temporal.io/abhinav_verma/32/4887_2.png) [@Abhinav\_Verma](https://community.temporal.io/u/Abhinav_Verma)\
**Post date:** [November 18, 2025, 3:23pm UTC](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647/20 "2025-11-18T15:23:58Z")

</div>

> [@antonio.perez](#):
>
> ```auto
> Promise<Boolean> result = Async.function(activity::execute);
> result.thenApply(
> (Boolean r) -> {
> done = true;
> return r;
> }).exceptionally((ex -> {
> 
> //your logic to handle the failure here
> done = false;
> return null;
> }));
> 
> Workflow.await(() -> done || signal_received);
> 
> ```

@antonio.perez@maxim Is this workflow code deterministic.

**Live Execution**

- Activity runs

- Callback (`thenApply`) fires

- `done` is set to `true`

- `await()` resume

- Workflow moves forward

**Replay Execution (Happens later)**

During replay:

- Activity **does not run**

- As a result `thenApply(...)` is **never triggered**

- `done.set(true)` **never happens**

- `done.get()` remains **false**

But since:

- `done` = false

- `workflowPaused` = false

The workflow enters an **infinite wait**.

Is my understanding correct ??

[Next page](https://community.temporal.io/t/best-practice-for-handling-lost-external-events-and-resuming-paused-workflows/18647.md?page=2)
