WoGu
WG008ERRORDeterminism

System.getenv() 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.getenv().

Why This Matters

Environment variables are properties of the worker process, 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 an environment variable directly, replay re-executes that read and may see a different value than the original execution did (a config change, 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 region = System.getenv("REGION");
  }
}

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() {
    // WG008 still fires: WoGu's call-graph analysis follows this call into
    // ConfigService.region() and finds System.getenv() inside it.
    configService.region();
  }
}
 
class ConfigService {
  String region() {
    return System.getenv("REGION");
  }
}

Compliant Example

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

False Positives

WG008 uses the same call-graph analysis as WG001WG007: 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.getenv() along the way. Matching is by method name only, so both the no-argument and single-argument (System.getenv(String)) overloads are flagged identically — there's no deterministic overload of this method to exempt.

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 WG008 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.getenv() specifically. If you believe WG008 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.