WoGu
WG002ERRORDeterminism

Thread.sleep() inside Workflow

Engine
Temporal Java SDK
Since
0.2.0
Auto Fix
Not Available
Category Range
WG001–WG099

Description

A Temporal workflow implementation — or any code reachable from a workflow's entry point, however many method calls away — calls Thread.sleep(...).

Why This Matters

A Temporal worker executes many workflow tasks concurrently on a small, shared pool of threads. Thread.sleep() blocks whichever worker thread happens to be executing the workflow task for its entire duration, holding that thread hostage instead of yielding it back to the pool. At any real scale this starves the worker of capacity to make progress on other workflows.

It's also the wrong tool for the job on its own terms: Temporal's programming model expects a workflow to express "wait" as a durable, replayable timer, not as a blocked thread. A blocked thread has no representation in workflow history — there is nothing for replay to reconstruct — so relying on it, rather than a timer Temporal itself tracks, works against how the workflow's progress is meant to be recorded and resumed.

io.temporal.workflow.Workflow.sleep(Duration) is the correct replacement: it registers a durable timer recorded in workflow history, does not block the worker thread, and resumes correctly on replay regardless of how much real wall-clock time has passed.

Violation Example

public class PaymentWorkflowImpl implements PaymentWorkflow {
  @Override
  public void processPayment() throws InterruptedException {
    Thread.sleep(5000);
  }
}

This is flagged even when the call is several methods away from the workflow entry point:

public class PaymentWorkflowImpl implements PaymentWorkflow {
  private final PaymentService paymentService = new PaymentService();
 
  @Override
  public void processPayment() {
    // WG002 still fires: WoGu's call-graph analysis follows this call into
    // PaymentService.waitForSettlement() and finds Thread.sleep() inside it.
    paymentService.waitForSettlement();
  }
}
 
class PaymentService {
  void waitForSettlement() throws Exception {
    Thread.sleep(5000);
  }
}

The report renders the full path from the workflow entry point down to the blocking call:

PaymentWorkflow.process()

PaymentService.waitForSettlement()

Thread.sleep()

Compliant Example

import io.temporal.workflow.Workflow;
import java.time.Duration;
 
public class PaymentWorkflowImpl implements PaymentWorkflow {
  @Override
  public void processPayment() {
    Workflow.sleep(Duration.ofSeconds(5));
  }
}

False Positives

WG002 uses the same call-graph analysis as WG001: starting from a workflow's entry-point method(s), it follows every method call that can be resolved to another method's source within the same project, however many hops deep, looking for Thread.sleep(...) along the way.

As with WG001, this analysis intentionally stops, without reporting anything past that point, when it reaches a call it cannot resolve to source it can see — most notably, a call to an Activity made through the activity's interface (the normal way Temporal activities are invoked): WoGu resolves such a call against the interface's method declaration, which has no body to look inside, so the activity's real implementation (and anything it does, including its own Thread.sleep() calls) is never reached. A call into a compiled dependency, or one resolved only through reflection or dynamic dispatch, is an equally intentional stopping point.

This means WG002 can produce false negatives (a real violation hidden behind a call it can't see into) but is designed to avoid false positives — it will not flag a call just because it looks superficially similar to Thread.sleep(); it must resolve to that exact method on java.lang.Thread (an explicit import is never required, since java.lang classes are always in scope, but an import of a different class also named Thread correctly prevents a false match). If you believe WG002 is flagging or missing something incorrectly, please open an issue with a minimal reproduction.

Found an issue with this rule's detection? Open an issue with a minimal reproduction.