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");
}
}Recommended Fix
Read configuration outside the workflow or via an Activity. A value read once outside the workflow (e.g. passed in as a workflow argument) or read inside an Activity (which is not replayed) is stable across replay by construction.
False Positives
WG008 uses the same call-graph analysis as WG001–WG007: 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.