Skip to main content

Determinism and non-deterministic errors

Orchestrators must be deterministic. This page explains what that means, why Sixpack requires it, and the common mistakes that cause non-deterministic errors. The behaviour applies to every SDK; for exact helper methods and imports, see the SDK-specific pages:

Why Orchestrators must be deterministic

This requirement applies only to Orchestrators. Generators may freely access external systems, read time, and use randomness - only Orchestrators are constrained.

The reason is replay. Rather than keep an Orchestrator running in memory from start to finish, Sixpack records each step it takes. Each time the Orchestrator resumes - after a request, after a Supplier restart, or on a retry - Sixpack re-runs its code from the beginning, feeding back the results of the steps already completed until execution reaches where it left off. This is how an Orchestrator can be stopped and resumed without losing progress (see Task lifecycle and retries).

Replay only rebuilds the correct state if the code follows the same path every time. Recorded steps - request calls and the SDK's deterministic helpers - are not run again; their results are fed back in order. But an ordinary non-deterministic call, such as reading the current time, drawing a random number, or making a direct HTTP request, is not recorded, so it really does run again and may return a different value. If that value changes which steps run, or the order they run in, replay no longer matches what was recorded and the task fails.

What deterministic means

An Orchestrator is deterministic when the same inputs always drive the same sequence of steps. In practice, an Orchestrator:

  • must not access external systems directly - reach them only through Generators with a request method
  • must not read time or randomness directly - use a deterministic source instead (the helpers your SDK provides, or a Generator)

Common mistakes

  • Calling a REST API from the Orchestrator - an unrecorded side effect that repeats on every replay and can return a different response each time. Do the call in a Generator instead.
  • Using randomness in the Orchestrator - a random value is redrawn on replay and can change a branch. Use a deterministic random helper, or get the value from a Generator.
  • Depending on network or disk reads - a file, database, or remote read can return different results on replay. Read it in a Generator, whose result Sixpack records and reuses.