open-feature / open-feature/go-sdk

[BUG] EvaluationAPI.Shutdown does not reset API state, so isolated instances retain providers, hooks, context and handlers

Open
#554 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

`EvaluationAPI.Shutdown` shuts providers down, calls `eventExecutor.shutdown()` and `unbindAllProvidersLocked()`, and returns:

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

It never assigns `a.hks`, `a.evalCtx`, `a.defaultProvider` (not reset to `NoopProvider{}`), or `a.domainProviders`. And `eventExecutor.shutdown()` only touches channels and goroutines — never `apiRegistry`, `scopedRegistry`, `states`, or the provider references:

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

On an isolated instance, after `Shutdown`:

```
default provider still active after Shutdown = true (a reset instance would return the default value)
global evaluation context retained = map[global:yes] (targetingKey "tk")
```

Domain providers stay bound, and API-level hooks still run.

The global path masks this. `openfeature.Shutdown()` → `resetSingletonWithContext` builds a **new** `*EvaluationAPI`, swaps it in, and only then shuts the old one down — a clean slate by replacement, not by reset:

https://github.com/open-feature/go-sdk/blob/6b0824c6c7b9b69e40f26aedcc4406ad4d3a5cec/openfeature/openfeature.go#L32-L40

But `isolated.NewAPI()` hands the caller an `*EvaluationAPI` they hold directly, so they get the unreset object. The doc comments on `Shutdown`/`ShutdownWithContext` promise "resets the state of the API, removing all hooks, event handlers, and providers" — true globally, not true of the method itself.

## Expected Behavior

Per [1.6.2](https://openfeature.dev/specification/sections/flag-evaluation#requirement-162), shutdown **MUST** reset all state of the API — hooks, event handlers, evaluation context, transaction context propagators, providers. And per [1.8.2](https://openfeature.dev/specification/sections/flag-evaluation#requirement-182), isolated instances **MUST** conform to the same API contract as the global singleton, *including shutdown functionality*.

Java resets in place, on the same object the caller holds, and that path is shared by `OpenFeatureAPI.createIsolated()`:

```java
providerRepository = new ProviderRepository(this);
eventSupport = new EventSupport();
clearHooks();
setEvaluationContext(ImmutableContext.EMPTY);
setTransactionContextPropagator(new NoOpTransactionContextPropagator());
```

Python does the equivalent (`clear_providers()`, `clear_hooks()`, `clear_evaluation_context()`, `clear_transaction_context_propagator()`).

(Transaction context propagators are N/A here — Go's are `context.Context`-based rather than API state.)

## Steps to reproduce

```go
api := isolated.NewAPI()
ctx := context.Background()

api.SetProviderAndWait(ctx, someProvider) // returns !defaultValue, so it's distinguishable from Noop
api.SetEvaluationContext(of.NewEvaluationContext("tk", map[string]any{"global": "yes"}))
api.Shutdown(ctx)

api.NewClient().Boolean(ctx, "any", false, of.EvaluationContext{}) // still hits someProvider
```

Contributor guide

Open the contributing guide

Research direction

Start in openfeature/openfeature_api.go at Shutdown and ShutdownWithContext, then compare the isolated.NewAPI path with the replacement flow in openfeature/openfeature.go and event_executor.go. Reproduce the isolated-instance scenario from the issue and verify that shutdown removes hooks, handlers, evaluation context, and providers so later evaluations use the reset default behavior.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.