feat(observability): one stable user id — Sentry now knows who the caller is - #247
Merged
Merged
Conversation
…ller is `grep setUser` over this repo returned nothing. PostHog identified on the DB user id and Sentry got that id only inside a free-text context blob, so no Sentry issue was attributable to an account: "users affected" was structurally zero and `user.id:<id>` search matched nothing. Two places, both the single place their side learns the identity: the browser sets it on every session transition in PostHogClientProvider (outside the PostHog-configured guard, because Sentry works even where PostHog is unset), and the server sets it in withRoute right after `auth.api.getSession` — which covers every authenticated API route at once and clears the field for anonymous requests. ID only: same `users.id` across Sentry, PostHog and Postgres, no email, no name, sendDefaultPii stays off. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.
Why
PostHog identifies on
users.id(identity.ts:112) and Sentry was handed that id only as afree-text context blob (
posthog_distinct_idinsidesetContext('posthog', …)). Sentry doesnothing with context: "users affected" counts,
user.id:<id>search and issue-to-accountattribution all read the user field, which nothing ever set. So every Caramel issue was
structurally anonymous — you could see that a route threw, never for whom.
Found during the
/paid-user-watchfleet run (2026-09-16), whose whole premise is that you must beable to read one user's story across systems before you judge them.
What changes
The id is set in the one place each side learns the identity — not sprinkled at call sites.
Browser —
PostHogClientProvider, on every session transition. Deliberately moved above theactiveRefguard: Sentry and PostHog are separately configured, and a deploy without a PostHog keymust still produce attributable Sentry issues. (That guard is exactly why the old code had no
identity call on this path at all.)
setSentryUser(null)on logout, so the next anonymous visitoron a shared device does not keep reporting as the account that just left.
Server —
withRoute, immediately afterauth.api.getSession. That single line covers everyroute that declares
auth, present and future, and clears the field for an anonymousauth: 'optional'request so a reused server context cannot leak the previous caller's identityonto someone else's error.
ID only. The same
users.idin Sentry, PostHog and Postgres — one join key across all three.No email, no name;
sendDefaultPiistays off.Tests
tests/unit/stable-user-id.test.ts— 4 cases: the payload is{id}and nothing else, logout clearsit, an authenticated
withRouterequest is attributed to the DB user id, and an anonymousauth: 'optional'request clears it.tests/unit/withRoute.test.ts's@sentry/nextjsmock gainssetUser(it previously exported onlycaptureException, so the auth tests faulted on the missing export).Local gates:
tsc --noEmitclean ·prettier --checkclean · full app unit suite green, 79 files/ 723 tests.
🤖 Generated with Claude Code