fix(core): clamp uuidv7 RNG output to a valid uint32 so a nonconformant Math.random cannot crash event capture - #4616
Open
shahidrogers wants to merge 1 commit into
Conversation
A nonconformant Math.random() that returns >= 1 or NaN (e.g. Hermes on Android, whose Math.random is built on C++ std::uniform_real_distribution — documented to occasionally return its upper bound) pushes the uuidv7 random fields out of range, making fromFieldsV7 throw 'RangeError: invalid field value' on every capture and crash-looping RN apps during the internal event-queue flush. Clamp nextUint32() with '>>> 0' so a bad random value degrades entropy for that id instead of throwing; timestamp bits untouched, ids remain spec-valid UUIDv7.
shahidrogers
marked this pull request as draft
August 24, 2026 04:06
marandaneto
marked this pull request as ready for review
August 24, 2026 07:58
Member
|
thanks @shahidrogers and good catch |
marandaneto
reviewed
Aug 24, 2026
| @@ -0,0 +1,5 @@ | |||
| --- | |||
| '@posthog/core': patch | |||
Member
There was a problem hiding this comment.
blocking: This changeset only releases @posthog/core, so consumers can publish without requiring the fixed core version. Please also add patch entries for every package whose runtime directly uses this UUIDv7 generator, inherits the core capture path, or ships the affected posthog-node: @posthog/ai, @posthog/convex, @posthog/mcp, @posthog/next, @posthog/nuxt, posthog-js-lite, posthog-node, and posthog-react-native.
marandaneto
approved these changes
Aug 24, 2026
Member
|
this lib is vendored so LiosK/uuidv7#17 |
Member
you need to sign your commits and force push so we can merge your pr, thanks again |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The vendored UUIDv7 generator in
@posthog/corebuilds its random fields from aMath.random()-basednextUint32()(Web Crypto is deliberately disabled for React Native compatibility). If the environment'sMath.random()ever returns a nonconformant value — ≥ 1.0 or NaN —nextUint32()produces a value outside[0, 2^32), andUUID.fromFieldsV7throwsRangeError: invalid field value.This is not hypothetical. On React Native Android, Hermes implements
Math.randomwith C++std::uniform_real_distribution(facebook/hermes#1169), which is documented to occasionally return its upper bound (1.0) in most standard-library implementations. In our production fintech app (posthog-react-native 4.61.4 / @posthog/core 1.46.7) affected devices hit the throw insidePromiseQueue.add → uuidv7()during the SDK's internal event-queue flush — a code path the host app cannot wrap in a try/catch — producing a fatal startup crash loop ~1.5 s after every cold start (Crashlytics: repeatedRangeError: invalid field valuewith thefromFieldsV7 → generateOrAbortCore → generateOrResetCore → generate → uuidv7 → add → addPendingPromise → flushInternal → flushJS stack). Reinstalling the app does not help; those users are hard-locked out. Related older report: #710.Changes
packages/core/src/vendor/uuidv7.ts: clampgetDefaultRandom().nextUint32()with>>> 0(ToUint32). A misbehavingMath.randomnow degrades entropy for that one id (NaN → 0, ≥ 2^32 wraps) instead of throwing. The timestamp bits are untouched and every downstream field (randA ≤ 0xfff,randBHi < 2^30,randBLo ≤ 0xffffffff, counter ≤ MAX_COUNTER) is mathematically in range, so generated ids remain spec-valid UUIDv7. Conformant engines are unaffected: for in-range inputs>>> 0is an identity.packages/core/src/vendor/uuidv7.spec.ts: new spec assertingV7Generator.generate()returns a well-formed v7 UUID whenMath.random()is stubbed to1.0,1.5, andNaN(all three throw today), plus a sanity check with the realMath.random.@posthog/core: patch).Release info Sub-libraries affected
Libraries affected
The changed package is
@posthog/core, so every SDK consuming it picks the fix up via the changesetsupdateInternalDependenciesflow. The consumer where the crash was observed:Checklist
Math.random; only nonconforming values change behavior, from throw to wrap)>>> 0)If releasing new changes
.changeset/fix-uuidv7-clamp-nextuint32.md— authored manually, same format aspnpm changesetoutput)🤖 Agent context
Autonomy: Human-driven (agent-assisted)
unixTsMsingenerateOrAbortCorewith a distinct error message, production stacks prove the failing fields are RNG-derived, so onlynextUint32needs guarding.Math.randomstubbed to 1.0 / 1.5 / NaN — all previously threw, all now produce regex-valid v7 UUIDs. Full monorepo test suite not run locally; relying on CI.