open-feature / open-feature/go-sdk
[BUG] multiprovider: re-initializing a Provider after Shutdown panics with "close of closed channel"
- Dominant language
- Go
- Stars
- 249
- Forks
- 61
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 20
Description
## Observed behavior
`outboundEvents` is created once in `NewProvider`:
https://github.com/open-feature/go-sdk/blob/6b0824c6c7b9b69e40f26aedcc4406ad4d3a5cec/openfeature/multi/multiprovider.go#L258-L266
…but it is closed on every teardown — by `InitWithContext` itself on the no-event path:
https://github.com/open-feature/go-sdk/blob/6b0824c6c7b9b69e40f26aedcc4406ad4d3a5cec/openfeature/multi/multiprovider.go#L417-L430
…or by `forwardProviderEvents` on the event path:
https://github.com/open-feature/go-sdk/blob/6b0824c6c7b9b69e40f26aedcc4406ad4d3a5cec/openfeature/multi/multiprovider.go#L438-L441
Re-initialization is legal and routine in this SDK (`SetProvider` initializes whatever it is handed), so an `Init → Shutdown → Init` cycle double-closes the channel:
```
PANIC on re-Init after Shutdown: close of closed channel
```
With event-emitting inner providers the panic happens on a background goroutine (`forwardProviderEvents`) rather than in the caller, which **no caller can recover** — it takes the process down:
```
panic: close of closed channel
multi.(*Provider).forwardProviderEvents multiprovider.go:501
created by multi.(*Provider).InitWithContext multiprovider.go:422
```
## Expected Behavior
`InitWithContext` should be safe to call on a previously shut-down `Provider`. Either:
- create `outboundEvents` per initialization rather than per construction, or
- refuse re-initialization with an error.
Either way, no panic and no unrecoverable background crash.
## Steps to reproduce
```go
mp, _ := multi.NewProvider(multi.StrategyFirstMatch,
multi.WithProvider("a", memprovider.NewInMemoryProvider(map[string]memprovider.InMemoryFlag{})))
ctx := context.Background()
mp.InitWithContext(ctx, of.EvaluationContext{})
mp.ShutdownWithContext(ctx)
mp.InitWithContext(ctx, of.EvaluationContext{}) // panic: close of closed channel
```
Contributor guide
Research direction
Start in openfeature/multi/multiprovider.go at NewProvider, InitWithContext, ShutdownWithContext, and forwardProviderEvents, then run the provided Init → Shutdown → Init reproduction. Trace both the no-event and event paths and add coverage for re-initialization; done means repeated initialization produces no close-of-closed-channel panic, including on background event forwarding.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- backend-api-design
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100