# Avoiding Non-Determinism in Workflows
Source: https://docs.chain.link/cre/concepts/non-determinism-go
Last Updated: 2026-04-20

> For the complete documentation index, see [llms.txt](/llms.txt).

> **NOTE: TL;DR**
>
> In DON mode, all nodes must execute identical code paths to reach consensus. Non-deterministic code causes nodes to
> generate different request IDs, breaking consensus. This guide shows common pitfalls in Go and how to avoid them.

## The problem: Why determinism matters

When your workflow runs in DON mode, multiple nodes execute the same code independently. These nodes must reach consensus on the results before proceeding. **If nodes execute different code paths, they generate different request IDs for capability calls, and consensus fails.**

The failure pattern: Code diverges → Different request IDs → No quorum → Workflow fails

## Quick reference: Common pitfalls

| Don't Use                        | Use Instead                                          |
| -------------------------------- | ---------------------------------------------------- |
| Direct map iteration             | Sort keys first, then iterate (`cre.OrderedEntries`) |
| `encoding/json` v2               | `encoding/json` v1                                   |
| Protocol Buffers `proto.Marshal` | `proto.MarshalOptions{Deterministic: true}`          |
| `select` with multiple channels  | Process channels in deterministic order              |
| `time.Now()` or `time` package   | `runtime.Now()`                                      |
| Go's `rand` package              | `runtime.Rand()`                                     |
| LLM free-text responses          | Structured output with field-level consensus         |

## 1. Map iteration

Go maps are **designed to iterate in random order** for security reasons. Each time you iterate over a map, the order may be different. This means different nodes will process items in different sequences, leading to divergent capability calls and consensus failure.

Use `cre.OrderedEntries` from the SDK to iterate over map entries in sorted key order. It handles the sorting for you in a single range expression:

```go
import "github.com/smartcontractkit/cre-sdk-go/cre"

prices := map[string]float64{"BTC": 50000, "ETH": 3000, "SOL": 100}

for token, price := range cre.OrderedEntries(prices) {
  processPrice(token, price)
}
```

For maps with keys that don't implement `cmp.Ordered` (such as structs), use `cre.OrderedEntriesFunc` with a custom comparator:

```go
import (
  "cmp"
  "github.com/smartcontractkit/cre-sdk-go/cre"
)

type Asset struct{ symbol string }

assets := map[Asset]float64{{symbol: "BTC"}: 50000, {symbol: "ETH"}: 3000}

for asset, price := range cre.OrderedEntriesFunc(assets, func(a, b Asset) int {
  return cmp.Compare(a.symbol, b.symbol)
}) {
  processPrice(asset, price)
}
```

## 2. JSON and data serialization

### JSON v2 non-determinism

The `encoding/json` v2 library uses random hashing for field order in hashmaps, making serialization non-deterministic. The same data structure can serialize to different JSON strings on different nodes.

**The solution:** Use `encoding/json` v1, which provides deterministic field ordering.

### Protocol Buffers serialization

The default `proto.Marshal` function does not guarantee deterministic output. Fields may be serialized in different orders across nodes.

**The solution:** Use `proto.MarshalOptions{Deterministic: true}.Marshal()` to ensure consistent serialization order across all nodes.

## 3. Concurrency and channel selection

Go's `select` statement with multiple channels introduces non-determinism. When multiple channels are ready, `select` picks one at random. Different nodes may select different channels, causing code paths to diverge.

**The problem:** `select` with multiple ready channels picks randomly, breaking consensus.

**The solution:** Process channels in a **fixed, deterministic order** instead of using `select`. Check channels sequentially in a consistent order across all nodes.

## 4. Time and dates

Never use Go's `time` package functions in DON mode. Nodes have different system clocks, causing divergence when calling `time.Now()` or similar functions.

**The problem:** Using `time.Now()` returns different values on each node.

**The solution:** Use `runtime.Now()` from the CRE SDK, which provides DON Time—a consensus-derived timestamp that all nodes agree on. See [Time in CRE](/cre/guides/workflow/time-in-workflows-go) for details.

## 5. Random number generation

Go's built-in `rand` package generates different random sequences on each node, making it impossible to reach consensus on values that depend on randomness.

**The problem:** Each node generates different random values, breaking consensus.

**The solution:** Use `runtime.Rand()` from the CRE SDK, which provides consensus-safe random number generation. All nodes generate the same sequence of random values, enabling consensus. See [Using Randomness in Workflows](/cre/guides/workflow/using-randomness) for details.

## 6. Working with LLMs

Large Language Models (LLMs) generate different responses for the same prompt, even with temperature set to 0. This inherent non-determinism breaks consensus in workflows.

**The problem:** Free-text responses from LLMs will vary across nodes, making it impossible to reach agreement on the output.

**The solution:** Request **structured output** from the LLM (such as JSON with specific fields) rather than free-form text. Then use consensus aggregation on the structured fields. This approach allows nodes to agree on the key data points even if the exact text varies slightly.

## Best practices summary

### Do:

- Sort map keys before iteration (or use `cre.OrderedEntries` / `cre.OrderedEntriesFunc`)
- Use `encoding/json` v1 for deterministic JSON serialization
- Use `proto.MarshalOptions{Deterministic: true}` for Protocol Buffers
- Process channels in a fixed, deterministic order
- Use `runtime.Now()` for all time operations
- Use `runtime.Rand()` for random number generation
- Request structured output from LLMs

### Don't:

- Iterate over maps directly without sorting keys
- Use `encoding/json` v2 (uses random hashing)
- Use `proto.Marshal` without deterministic options
- Use `select` with multiple channels for decision-making
- Use `time.Now()` or other `time` package functions
- Use Go's `rand` package directly
- Rely on free-text LLM responses

## Related concepts

- **[Time in CRE](/cre/guides/workflow/time-in-workflows-go)**: Learn about DON Time and why `runtime.Now()` is required
- **[Using Randomness in Workflows](/cre/guides/workflow/using-randomness)**: Understand consensus-safe random number generation
- **[Consensus Computing](/cre/concepts/consensus-computing)**: Deep dive into how nodes reach agreement