From 2253ac141d1300ec641f64a9885a4abc3daa70b8 Mon Sep 17 00:00:00 2001 From: Sueun Cho Date: Fri, 26 Jun 2026 02:00:33 +0900 Subject: [PATCH] docs: correct context-cancellation claim on non-blocking SetProviderWithContext SetProviderWithContext and SetNamedProviderWithContext said they return an error if the context is cancelled during setup. They don't: both call api().SetProvider(ctx, provider), which returns synchronously only when the provider is nil or is already bound to a different API instance. The context is passed to the background initNew goroutine and never checked synchronously, so a cancelled context with a valid provider returns nil. Only the ...AndWait variants honor cancellation, via the select on ctx.Done() in SetProviderAndWait, and their docs already say so. The non-blocking behavior is intended: TestContextAwareInitialization's "async initialization returns immediately" subtest asserts SetProviderWithContext returns immediately without error, so this corrects the comment rather than the code. Added a subtest covering the cancelled-context case so the contract does not silently flip back. Signed-off-by: Sueun Cho --- openfeature/context_aware_test.go | 14 ++++++++++++++ openfeature/openfeature.go | 10 ++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/openfeature/context_aware_test.go b/openfeature/context_aware_test.go index fb507782..f7174142 100644 --- a/openfeature/context_aware_test.go +++ b/openfeature/context_aware_test.go @@ -134,6 +134,20 @@ func TestContextAwareInitialization(t *testing.T) { } }) + t.Run("cancelled context does not fail async setup", func(t *testing.T) { + // The non-blocking variants hand the context to the background + // initialization; they do not return an error when it is cancelled. + ctx, cancel := context.WithCancel(t.Context()) + cancel() + + if err := SetProviderWithContext(ctx, &testContextAwareProvider{}); err != nil { + t.Errorf("SetProviderWithContext with cancelled context: got %v, want nil", err) + } + if err := SetNamedProviderWithContext(ctx, "cancelled-domain", &testContextAwareProvider{}); err != nil { + t.Errorf("SetNamedProviderWithContext with cancelled context: got %v, want nil", err) + } + }) + t.Run("named provider with context works", func(t *testing.T) { namedProvider := &testContextAwareProvider{initDelay: 50 * time.Millisecond} diff --git a/openfeature/openfeature.go b/openfeature/openfeature.go index fa6ab5dd..be2ff517 100644 --- a/openfeature/openfeature.go +++ b/openfeature/openfeature.go @@ -70,7 +70,10 @@ func SetProviderAndWait(provider FeatureProvider) error { // SetProviderWithContext sets the default [FeatureProvider] with context-aware initialization. // If the provider implements ContextAwareStateHandler, InitWithContext will be called with the provided context. // Provider initialization is asynchronous and status can be checked from provider status. -// Returns an error immediately if provider is nil, or if context is cancelled during setup. +// Returns an error immediately if the provider is nil or is already bound to a different +// API instance. Initialization runs in the background, so a cancelled context does not make +// this function return an error; the context is passed to that background initialization, +// whose outcome is reported through provider status. // // Use this function for non-blocking provider setup with timeout control where you want // to continue application startup while the provider initializes in background. @@ -111,7 +114,10 @@ func SetNamedProviderAndWait(domain string, provider FeatureProvider) error { // SetNamedProviderWithContext sets a [FeatureProvider] mapped to the given [Client] domain with context-aware initialization. // If the provider implements ContextAwareStateHandler, InitWithContext will be called with the provided context. // Provider initialization is asynchronous and status can be checked from provider status. -// Returns an error immediately if provider is nil, or if context is cancelled during setup. +// Returns an error immediately if the provider is nil or is already bound to a different +// API instance. Initialization runs in the background, so a cancelled context does not make +// this function return an error; the context is passed to that background initialization, +// whose outcome is reported through provider status. // // Named providers allow different domains to use different feature flag providers, // enabling multi-tenant applications or microservice architectures.