WoGu
WG009ERRORDeterminism

System.getProperty() inside Workflow

Engine
Temporal Java SDK
Since
0.3.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 System.getProperty(...).

Why This Matters

System properties are properties of the worker's JVM, not of the workflow. Temporal workflows are replayed, sometimes on a different worker host or in a redeployed environment: whenever a worker needs to reconstruct a workflow's state, it re-executes the workflow's code from the beginning of event history. If workflow code reads a system property directly, replay re-executes that read and may see a different value than the original execution did (a different JVM configuration, a different host, a different deployment) — and if any decision in the workflow depends on that value, replay diverges from the original execution and raises a NonDeterministicException.

Violation Example

public class PaymentWorkflowImpl implements PaymentWorkflow {
  @Override
  public void processPayment() {
    String home = System.getProperty("user.home");
  }
}

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

public class PaymentWorkflowImpl implements PaymentWorkflow {
  private final ConfigService configService = new ConfigService();
 
  @Override
  public void processPayment() {
    // WG009 still fires: WoGu's call-graph analysis follows this call into
    // ConfigService.userHome() and finds System.getProperty() inside it.
    configService.userHome();
  }
}
 
class ConfigService {
  String userHome() {
    return System.getProperty("user.home");
  }
}

Compliant Example

public class PaymentWorkflowImpl implements PaymentWorkflow {
  private final ConfigActivity configActivity;
 
  @Override
  public void processPayment() {
    String home = configActivity.readUserHome();
  }
}
 
// Implemented outside the workflow; not subject to replay determinism.
class ConfigActivityImpl implements ConfigActivity {
  @Override
  public String readUserHome() {
    return System.getProperty("user.home");
  }
}

False Positives

WG009 uses the same call-graph analysis as WG001WG008: 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 System.getProperty(...) along the way. Matching is by method name only, so both the single-argument and two-argument (with a default value) overloads are flagged identically.

As with the other determinism rules, this analysis intentionally stops, without reporting anything past that point, at two kinds of boundary: a call it cannot resolve to source it can see, and a call that resolves into a Temporal Activity implementation (recognized by its @ActivityInterface/@ActivityMethod annotations, or by resolving only as far as the activity interface's bodyless method) — activity code is not replayed, so it is exempt from this rule by design.

This means WG009 can produce false negatives (a real violation hidden behind a call it can't see into) but is designed to avoid false positives — it must resolve to System.getProperty(...) specifically. If you believe WG009 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.