open-feature / open-feature/go-sdk

[BUG] multiprovider: NOT_READY and FATAL are absent from the state precedence table, so they resolve to READY

Closed
#544 1 comment 0 reactions 0 assignees View on GitHub
bug multi-provider
Dominant language
Go
Stars
250
Forks
62
Avg merge
2d 3h
Merged PRs (30d)
25

Description

## Observed behavior

The state precedence tables in `openfeature/multi/multiprovider.go` define only three states:

https://github.com/open-feature/go-sdk/blob/6b0824c6c7b9b69e40f26aedcc4406ad4d3a5cec/openfeature/multi/multiprovider.go#L119-L130

`of.NotReadyState` and `of.FatalState` are missing. `evaluateState()` does a bare `stateValues[s]` lookup, so a miss yields the zero value `0`, and `stateTable[0]` maps back to **`ReadyState`**. The two highest-precedence states in Appendix A become the lowest:

https://github.com/open-feature/go-sdk/blob/6b0824c6c7b9b69e40f26aedcc4406ad4d3a5cec/openfeature/multi/multiprovider.go#L529-L540

This is reachable on the ordinary startup path: `InitWithContext` explicitly sets every inner provider to `NotReadyState` before initializing it, which immediately flips the overall status to READY.

```
status BEFORE Init = "NOT_READY"
status DURING Init (inner NOT_READY) = "READY" <- want NOT_READY
status AFTER Init = "READY"
```

Separately, `FatalState` is **unrepresentable** in this package. `eventTypeToState` maps `of.ProviderError` → `of.ErrorState` unconditionally and ignores `ProviderEventDetails.ErrorCode`, unlike core's `statesMap`, which checks for `ProviderFatalCode`:

https://github.com/open-feature/go-sdk/blob/6b0824c6c7b9b69e40f26aedcc4406ad4d3a5cec/openfeature/openfeature_api.go#L518-L528

```
multi Status() after inner PROVIDER_ERROR{PROVIDER_FATAL} = "ERROR" <- want FATAL
```

## Expected Behavior

[Appendix A](https://github.com/open-feature/spec/blob/main/specification/appendix-a-included-utilities.md#multi-provider-status) ("Multi-Provider Status") defines the precedence order as **FATAL > NOT_READY > ERROR > STALE > READY**. Both states should be present in the tables, and `eventTypeToState` should map a `PROVIDER_ERROR` carrying error code `PROVIDER_FATAL` to `FatalState`, matching core's behaviour.

This matters beyond status reporting. Appendix A: *"The only statuses which affect evaluation behavior at the SDK client level are FATAL and NOT_READY. If a provider is in either of these states, evaluation will be skipped by the client and the default value will be returned."* Since `Client.evaluate` short-circuits only on `NotReadyState`/`FatalState`, this causes the client to evaluate against a fatal or not-yet-initialized multi-provider instead of returning the default value.

Note that fixing the maps alone is insufficient — `evaluateState` maps precedence back through a fixed `[3]of.State` array, so the array and both maps need to grow together.

## Steps to reproduce

```go
// slowInitProvider.Init sleeps for `delay`, then returns nil
slow := &slowInitProvider{delay: 700 * time.Millisecond}
mp, _ := multi.NewProvider(multi.StrategyFirstMatch, multi.WithProvider("slow", slow))

go mp.InitWithContext(context.Background(), of.EvaluationContext{})
time.Sleep(200 * time.Millisecond)

fmt.Println(mp.Status()) // "READY" while the only inner provider is still NOT_READY
```

For the FATAL half, have an inner provider emit `of.Event{EventType: of.ProviderError, ProviderEventDetails: of.ProviderEventDetails{ErrorCode: of.ProviderFatalCode}}` and observe `mp.Status()` report `ERROR` rather than `FATAL`.

Contributor guide

Open the contributing guide

Research direction

Start in openfeature/multi/multiprovider.go, reading the state precedence tables, evaluateState, eventTypeToState, and InitWithContext. Reproduce the slow-initialization and PROVIDER_FATAL scenarios described in the issue, then verify that the status precedence is FATAL > NOT_READY > ERROR > STALE > READY and that both cases cause the expected client behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
api, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.