open-feature / open-feature/go-sdk

[BUG] multiprovider: re-initializing a Provider after Shutdown panics with "close of closed channel"

Open
#548 0 comments 0 reactions 0 assignees View on GitHub
bug multi-provider Needs Triage
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.