Skip to content

fix(browser): run tracking hooks in finally - #406

Merged
sameerank merged 2 commits into
mainfrom
sameerank/browser-track-failed-evaluations
Oct 7, 2026
Merged

sameerank merged 2 commits into
mainfrom
sameerank/browser-track-failed-evaluations

Conversation

@sameerank

@sameerank sameerank commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

The browser tracking hooks run in OpenFeature's after stage. OpenFeature skips that stage when evaluation fails, so calls that return the application's fallback (for example, FLAG_NOT_FOUND or TYPE_MISMATCH) are missing from evaluation events.

Tracking in after can also record an assignment that a later after hook rejects. A tracking callback that throws can turn a successful evaluation into a fallback. Running tracking in finally lets it observe the final result and keeps tracking failures from changing that result.

Identified while reviewing the React Native hooks implementation.

Changes

  • Run evaluation, exposure, and RUM tracking in finally, which receives the final details returned to the application, for both DatadogProvider and manually registered hooks on DatadogCoreProvider.
  • Forward finally through the tracking lifecycle controller and the online provider's effective-context wrapper. Initialization, shutdown, and RUM-enriched context behavior stay intact.
  • Populate error.message with the OpenFeature error code only. Do not upload free-form provider or hook error messages, which can contain application data.
  • Record successful and failed evaluations once. Exposure and RUM hooks skip errors and results without a variant. Exposure logging retains its existing metadata checks and deduplication.
  • Do not report or deduplicate assignments discarded by a throwing after hook. A throwing RUM callback no longer changes the flag result or prevents other finally hooks from running.
  • Keep the public API and core package unchanged.

Server SDK comparison

Checked the server evaluation-event implementations directly. All use the terminal hook stage to include successful and failed evaluations:

SDK Evaluation-event hook
Go Finally
Python finally_after
Java finallyAfter
Ruby finally
Node.js finally

Error text is not uniform across these implementations. Python and Ruby use error codes, and Node.js captures the error code rather than the raw message. Java allows raw messages only with full-data consent and otherwise uses the code. This PR uses code-only errors for the browser; unlike the linked RN fix, it does not prefer errorMessage.

This does not mean every server tracking hook uses finally: the Go exposure hook uses After. Moving all browser tracking to finally is a deliberate choice to observe the final result and isolate tracking errors, consistent with the OpenFeature hook lifecycle.

This change aligns evaluation coverage with the server hooks. It does not adopt their separate consent, buffering, or delivery implementations.

Test instructions

Run the browser suite:

node node_modules/jest/bin/jest.js --config packages/browser/jest.config.js --runInBand --watchman=false

Local results: 23 suites passed; the full run had 353 passing tests and three failures in the unchanged fetchRetry.spec.ts, with stack overflows in the source-map dependency. All 46 fetch-retry tests passed when that suite was run separately. All tracking tests passed.

The regression tests use real OpenFeature clients with mocked HTTP transport for both providers. They cover missing flags, type mismatches, throwing before and after hooks, successful counts, mixed success/error aggregation, error-message exclusion, RUM-enriched context, and shutdown/reinitialization. They also verify that discarded assignments do not fill the exposure cache and that a throwing RUM callback preserves the result and other tracking. The regression cases failed before the corresponding fixes.

Direct hook tests verify that exposure and RUM tracking skip error results even when a variant is present, and skip results without a variant.

Also passed browser source type checking, test type checking with --rootDir . (the test suite imports core source files), changed-file Biome and Prettier checks, and git diff --check.

Checklist

  • Updated Documentation (not required: no public API change)
  • Tested locally
  • Tested on staging
  • Added unit tests for this change.
  • Added e2e/integration tests for this change.

@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

OpenFeature Browser Entrypoint Bundle Sizes

Measured from the Vite production output after installing packed @datadog/flagging-core and @datadog/openfeature-browser tarballs.

Entrypoint HTML JS Assets Raw JS Gzip JS Protobuf Markers
root provider smoke index.html 11 82.9 KiB 29.7 KiB no
precomputed configuration precomputed.html 6 6.0 KiB 2.8 KiB no
rules-based configuration protobuf.html 13 176.9 KiB 56.2 KiB yes

Default and precomputed entrypoints are expected to keep Protobuf-ES out of their bundles. Rules-based entrypoints are expected to include protobuf markers as a positive control. The marker check is a packed-artifact backstop; the source import boundary is enforced by packages/core/test/entrypoint-boundaries.spec.ts.

OpenFeature Browser Tracking Hook Bundle Sizes

Synthetic entrypoints import and call tracking hook factories from the packed @datadog/openfeature-browser/rules-based ESM package. Deltas are measured against the no-hook baseline from the same Vite production build.

Scenario HTML JS Assets Raw JS Raw Δ Gzip JS Gzip Δ
tracking hook baseline tracking-baseline.html 5 2.4 KiB 0 B 1.4 KiB 0 B
exposure logging hook tracking-exposure.html 10 39.8 KiB +37.4 KiB 15.6 KiB +14.2 KiB
evaluation logging hook tracking-evaluation.html 10 49.2 KiB +46.7 KiB 18.2 KiB +16.8 KiB
RUM tracking hook tracking-rum.html 8 4.8 KiB +2.4 KiB 2.7 KiB +1.3 KiB
all tracking hooks tracking-all.html 12 55.9 KiB +53.4 KiB 20.9 KiB +19.5 KiB

This report shows current PR artifact sizes only; it does not compare against the base branch.

@sameerank
sameerank force-pushed the sameerank/browser-track-failed-evaluations branch from addeb3a to 2ee684d Compare October 2, 2026 22:18
@sameerank sameerank changed the title fix(browser): track failed evaluations in finally hooks fix(browser): run tracking hooks in finally Oct 2, 2026
@sameerank
sameerank force-pushed the sameerank/browser-track-failed-evaluations branch from 37a235f to 163b1af Compare October 3, 2026 01:47
Comment thread packages/browser/src/openfeature/flagEvaluations.ts
@sameerank
sameerank marked this pull request as ready for review October 3, 2026 02:53
@sameerank
sameerank requested a review from a team as a code owner October 3, 2026 02:53
@sameerank
sameerank requested review from danyal002, greghuels and vjfridge and removed request for a team October 3, 2026 02:53
@sameerank
sameerank merged commit 81d4ebe into main Oct 7, 2026
4 checks passed
@sameerank
sameerank deleted the sameerank/browser-track-failed-evaluations branch October 7, 2026 15:37
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.

2 participants