Skip to content

chore!: pass the load result to the feature flags callback - #877

Open
turnipdabeets wants to merge 2 commits into
chore/v4-remove-deprecatedfrom
chore/v4-flags-loaded-result
Open

turnipdabeets wants to merge 2 commits into
chore/v4-remove-deprecatedfrom
chore/v4-flags-loaded-result

Conversation

@turnipdabeets

@turnipdabeets turnipdabeets commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Note

Stacked on the 4.0 integration PR #871. Merges into chore/v4-remove-deprecated, not main.

💡 Motivation and Context

Brings the Android flags callback to parity with posthog-ios 4.0 (PostHogFeatureFlagsLoaded) and Flutter 6.

PostHogConfig.onFeatureFlags and reloadFeatureFlags() now take a new PostHogFeatureFlagsCallback. Its loaded() receives a PostHogFeatureFlagsLoaded with:

  • flags: the keys of the enabled flags
  • variants: their values (true or the variant string)
  • errorsLoading: whether the latest load failed (the values are then the last known ones)

Every path that invoked the old callback now passes an accurate result, including failed requests, a disabled SDK, and bootstrap.

config.onFeatureFlags = PostHogFeatureFlagsCallback { loaded ->
    if (!loaded.errorsLoading && loaded.variants["new-checkout"] == "test") {
        // ...
    }
}

Why a new type instead of changing PostHogOnFeatureFlags: core's public types are also part of posthog-server's API. Changing PostHogOnFeatureFlags.loaded() would force a server major for a callback the server can only fill with empty data (it evaluates flags per call). So:

  • PostHogOnFeatureFlags moves from core into posthog-server, same package and shape, so server users' source and binaries keep working. Android no longer sees it.
  • The server already calls its own callback directly, so it no longer passes one to the core config (core's stateless client never called it).
  • posthog-server gets a minor bump, since it now depends on core 7.0.
  • The server can drop or replace it in its own major.

The other core types the server exposes (FeatureFlagResult, PostHogBeforeSend, PostHogEncryption, PostHogIntegration) are unchanged.

For reviewers: a blank distinctId reports errorsLoading = true, since no request is made.

💚 How did you test it?

  • ./gradlew spotlessCheck apiCheck :posthog:test :posthog-server:test :posthog-android:testDebugUnitTest pass, and the Android sample compiles.
  • PostHogRemoteConfigTest and PostHogBootstrapTest assert the result passed on success, failure, and bootstrap.
  • The server's tests are unchanged apart from asCoreConfig no longer setting the core callback.
  • posthog-server.api only gains PostHogOnFeatureFlags (moved in, unchanged).

📝 Checklist

  • All commits are signed (see Commit signing)
  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Ran pnpm changeset to generate a changeset file

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Claude Code (Claude Opus 5.5) implemented the change after a cross-SDK flags API review (iOS, Android, Flutter, React Native). A first version changed PostHogOnFeatureFlags in place and bumped the server to a major; it was reworked to move the old type into posthog-server so the server stays untouched until its own major.

🤖 Generated with Claude Code

PostHogConfig.onFeatureFlags and reloadFeatureFlags() now take a PostHogFeatureFlagsCallback, whose loaded() receives a PostHogFeatureFlagsLoaded with the enabled flags, their values, and errorsLoading, matching posthog-ios 4.0. Every path that invoked the callback passes an accurate result, including failures, a disabled SDK, and bootstrap.

PostHogOnFeatureFlags moves to posthog-server under the same name, so the server's API is unchanged and it can drop the type in its own major.

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostHog Review · Standard · reviewed 90beca5: 2 Should fix.

See this review and its findings in PostHog Review. Comment @posthog review --deep for a Deep review. A Deep review takes longer and looks deeper than a Standard review.

onFeatureFlags?.runSafely()
internalOnFeatureFlags?.let { runSafely(it) }
onFeatureFlags?.let { callback ->
runSafely { callback.loaded(PostHogFeatureFlagsLoaded(getFeatureFlags() ?: emptyMap(), errorsLoading)) }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 · Hydrate cached flags before constructing the callback result

On a fresh SDK instance with persisted flags, an offline or failed first load returns empty variants: getFeatureFlags() only copies the in-memory field, while getFeatureFlagResult() hydrates the cache lazily. The new completion therefore omits the promised last known flags until a getter runs; hydrate the cache before constructing the result.

if (remoteConfig?.hasBootstrapFlags() == true) {
notifyFeatureFlagsCallback(internalOnFeatureFlagsLoaded)
notifyFeatureFlagsCallback(config.onFeatureFlags)
internalOnFeatureFlagsLoaded()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 · Restore exception isolation for the bootstrap internal callback

With bootstrap flags and session replay enabled, an exception from the replay handler now escapes internalOnFeatureFlagsLoaded() into setup's outer catch. This skips the user callback, initial config/flags request, and bootstrap identity reconciliation, leaving setup incomplete. The previous helper isolated this failure; catch and log the internal callback exception before continuing.

posthog-server calls its own PostHogOnFeatureFlags directly, and the core stateless client never calls config.onFeatureFlags. Bump posthog-server to minor since it now depends on core 7.0, and document that a bootstrapped onFeatureFlags runs on the setup() thread.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant