Fixing Nested Attribute Resolution in GrowthBook JS SDK Experiments

The GrowthBook JS SDK was skipping experiments when hash attributes used dotted notation (like 'user.id') on nested objects. A fix reuses the existing dotted-path resolver to handle both flat and nested attribute lookups consistently.
What
The getHashAttribute() function in the SDK performed only flat key lookups, so a hash attribute named 'user.id' would fail to resolve against a nested attributes object like { user: { id: '123' } }. The attribute fell through to an empty-hash fallback, causing experiments to be skipped. The fix introduces a resolveAttribute() helper that tries the literal key first (preserving backward compatibility for keys that literally contain dots), then falls back to dotted-path resolution using the existing getPath() utility.
Why it matters
Experiments should work consistently regardless of how developers structure their attributes. The SDK already had dotted-path resolution for targeting rules via getPath() in mongrule.ts, but the hash-attribute code never used it. Reusing getPath() follows DRY principles, adds zero new dependencies, and ensures feature parity across the codebase.
Who it's for
Any GrowthBook JS SDK user relying on nested attribute structures for experiment bucketing. Teams using object-shaped context (common in modern apps) will see experiments run reliably instead of being silently skipped.
When & where
This is a draft PR (#6172) against the growthbook/growthbook repository, tested on Node 24.16 with 679 passing jest tests across 15 suites. Three new regression tests verify nested resolution, literal-dotted key precedence, and missing nested attribute behavior.
How
The fix adds a resolveAttribute() helper that attempts attribute lookup in order: literal flat key, then dotted path traversal. This helper replaces direct attribute access in both the hash attribute and fallback attribute code paths. All existing tests pass, type checking and linting are clean, and the change is fully backward compatible.
Takeaway
Small, focused fixes that reuse existing utilities are often the best way to close gaps in SDK behavior. By leveraging getPath() instead of duplicating logic, the fix is maintainable, testable, and consistent with how the rest of the codebase handles nested data.
Draft PR: https://github.com/growthbook/growthbook/pull/6172
Building an AI agent?
I'm packaging how I ship them into one kit. Early access:
AI Agent Starter Kit →