open-feature / open-feature/go-sdk

[BUG] a domain client reports the default provider's status while its own provider is still initializing

Open
#550 0 comments 0 reactions 0 assignees View on GitHub
bug Needs Triage
Dominant language
Go
Stars
249
Forks
61
Avg merge
2d 10h
Merged PRs (30d)
20

Description

## Observed behavior

`loadState` falls back to the **default** provider's state when a domain has no entry of its own:

https://github.com/open-feature/go-sdk/blob/6b0824c6c7b9b69e40f26aedcc4406ad4d3a5cec/openfeature/event_executor.go#L181-L194

`e.states` is written only in `triggerEvent`, which runs from `initNew`'s goroutine *after* initialization completes. `registerNamedEventingProvider` never seeds a `NOT_READY` entry for the domain:

https://github.com/open-feature/go-sdk/blob/6b0824c6c7b9b69e40f26aedcc4406ad4d3a5cec/openfeature/event_executor.go#L208-L218

So between `SetNamedProvider("foo", p)` and `p`'s initialization finishing, a client on domain `foo` inherits the **default** provider's status. If the default provider is READY, the domain client reports READY, and `Client.evaluate` therefore skips the NOT_READY short-circuit and evaluates against a provider whose `Init` has not returned:

```
domain 'foo' state while its provider is still initializing = "READY" <- want NOT_READY
eval error during domain init = <- want PROVIDER_NOT_READY
```

## Expected Behavior

Per [1.7.1](https://openfeature.dev/specification/sections/flag-evaluation#requirement-171) / [1.7.3](https://openfeature.dev/specification/sections/flag-evaluation#requirement-173) and the §1.7 lifecycle diagram (`[*] --> NOT_READY`), a provider is READY only *after* it emits `PROVIDER_READY`. A domain with a registered-but-initializing provider should report `NOT_READY`, and should not inherit an unrelated provider's status.

All three sibling SDKs seed NOT_READY synchronously at registration, before init:

- **Java** — status lives on a per-provider `FeatureProviderStateManager` initialized to `NOT_READY`, installed into the domain map before init. Its `getProviderState(domain)` does fall back to the default state manager, but only when no provider is registered for that domain at all.
- **JS** — `new ProviderWrapper(provider, NOT_READY, ...)` stored before the init promise resolves.
- **Python** — `self._provider_status[provider] = ProviderStatus.NOT_READY` before `_initialize_provider`.

Seeding `e.states.Store(domain, NotReadyState)` in `registerNamedEventingProvider` (and the default equivalent in `registerDefaultProvider`) would match that behaviour.

## Steps to reproduce

```go
api := isolated.NewAPI() // same behaviour on the global singleton
ctx := context.Background()

api.SetProviderAndWait(ctx, readyProvider) // default -> READY
go api.SetProviderAndWait(ctx, slowProvider, of.WithDomain("foo")) // Init sleeps 800ms
time.Sleep(150 * time.Millisecond)

c := api.NewClient(of.WithDomain("foo"))
fmt.Println(c.State()) // "READY"
_, err := c.BooleanValueDetails(ctx, "f", false, of.EvaluationContext{})
fmt.Println(err) //
```

Contributor guide

Open the contributing guide

Research direction

Start in openfeature/event_executor.go at loadState, registerNamedEventingProvider, and registerDefaultProvider, then trace Client.evaluate. Reproduce the issue with isolated.NewAPI and the slow provider example; done means a registered domain provider reports NOT_READY during initialization and evaluation returns PROVIDER_NOT_READY instead of inheriting the default provider's state.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.