Skip to content

fix(changelog): resolve index-page hydration mismatch (Seer root-cause triage of open Sentry issues)#152

Open
rahulchhabria wants to merge 1 commit into
mainfrom
claude/sentry-issues-root-cause-x18x1k
Open

fix(changelog): resolve index-page hydration mismatch (Seer root-cause triage of open Sentry issues)#152
rahulchhabria wants to merge 1 commit into
mainfrom
claude/sentry-issues-root-cause-x18x1k

Conversation

@rahulchhabria

Copy link
Copy Markdown
Contributor

Summary

Pulled all 17 open (unresolved) issues from the Sentry changelog project (org sentry) and ran Seer root-cause analysis on each. This PR contains the one change that is a genuine, fixable application bug; the remaining issues are triaged directly in Sentry (see table) because their root causes are outside this codebase (bot/scanner traffic, third-party browser-injected scripts, a separate cloud function, deployment version skew, or infra/config).

Code change — CHANGELOG-7X (hydration mismatch on /changelog)

Seer root cause: The index page awaited dynamic data at the page top level, so the search-param-driven client feed (ChangelogList, which reads URL state via nuqs/useSearchParams) rendered outside any explicit Suspense boundary. Under cacheComponents: true, the statically prerendered tree then disagreed with the client resume, surfacing as TypeError: Cannot read properties of null (reading 'parentNode') in React's streaming reconciliation.

Fix: Keep the header + document metadata in a statically-prerenderable shell and stream the dynamic feed inside an explicit <Suspense> boundary — the idiomatic Cache Components pattern.

  • src/app/changelog/page.tsx — static shell + <Suspense>-wrapped <ChangelogFeed>
  • src/app/changelog/feedSkeleton.tsx — extracted shared feed skeleton (new)
  • src/app/changelog/loading.tsx — reuse the shared skeleton (DRY)

Verification: biome check clean, tsc --noEmit clean (only the pre-existing squiggle.png image-type note, unrelated), vitest run 25/25 passing. Full hydration confirmation requires a production deploy (hydration can't be reproduced in unit tests).

Not fixed here — framework-level (needs upstream Next.js)

CHANGELOG-1 / CHANGELOG-8B__next_metadata_boundary__ resume mismatch on /changelog/[slug]. Seer attributes this to a Next.js 16.2.6 + Turbopack async-metadata-streaming interaction (100% of events on Turbopack). The [slug] route is already Suspense-wrapped via loading.tsx, so there is no safe app-level structural fix — this should be tracked against a Next.js upgrade rather than patched speculatively. Left open in Sentry.

Triaged in Sentry (not app-code bugs)

Issue Root cause (Seer) Action
CHANGELOG-82 CONFIG/updateGapFiller — injected marketing-shell script, not in repo ignore
CHANGELOG-73 Brave/iOS injects window.__firefox__ script ignore
CHANGELOG-7W ProductFruits third-party fetch blocked ignore
CHANGELOG-9 Browser extension mutates DOM after RSC re-render ignore
CHANGELOG-48 Stale Next-Router-State-Tree across deploys (framework) ignore
CHANGELOG-7Z / 3T / 7Y / 85 "Failed to find Server Action" — bot POSTs / stale action IDs across deploys ignore
CHANGELOG-3W Scanner-injected _rsc param → FormData parse ignore
CHANGELOG-7J Twitter API 402 CreditsDepleted (separate changelog-twitter-poster cloud fn) ignore
CHANGELOG-8C ReadTimeout fetching feed.xml (same external cloud fn) ignore
CHANGELOG-84 DB endpoint unreachable from serverless runtime (infra) ignore
CHANGELOG-8D Preview deploy missing NEXTAUTH_SECRET (config) ignore

🤖 Generated with Claude Code


Generated by Claude Code

The changelog index awaited dynamic data at the page top level, so the
search-param-driven client feed (ChangelogList, which reads URL state via
nuqs/useSearchParams) was rendered outside any explicit Suspense boundary.
Under `cacheComponents`, the statically prerendered tree then disagreed with
the client resume, producing a hydration mismatch that surfaced in Sentry as
"TypeError: Cannot read properties of null (reading 'parentNode')"
(CHANGELOG-7X).

Keep the header and document metadata in a static shell and stream the
dynamic feed inside an explicit <Suspense> boundary. Extract the shared feed
skeleton so loading.tsx and the Suspense fallback stay in sync.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RjXWBRZkUCE3W9kXrcyYih
@vercel

vercel Bot commented Jul 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sentry-changelog Ready Ready Preview Jul 19, 2026 9:13pm

Request Review

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