Skip to content

fix: resolve Sentry hydration mismatch and suppress extension noise#439

Merged
ajitpratap0 merged 2 commits intomainfrom
fix/sentry-issues-hydration-noise
Mar 26, 2026
Merged

fix: resolve Sentry hydration mismatch and suppress extension noise#439
ajitpratap0 merged 2 commits intomainfrom
fix/sentry-issues-hydration-noise

Conversation

@ajitpratap0
Copy link
Copy Markdown
Owner

Summary

Fixes all 4 open Sentry issues from #434#437.

Real bug fix — FadeIn.tsx hydration mismatch (closes #436, #437)

Root cause: Framer Motion applies initial={{ opacity: 0, y: 16 }} via a layout effect before React hydrates. The server renders content at opacity: 1 (the animate state), but the client DOM gets set to opacity: 0 by Framer Motion before React checks for mismatches → hydration error in 8 events across 2 users.

Fix: Render a plain <div> on server + during the hydration pass (isMounted = false). After useEffect fires (post-hydration), swap to <motion.div> with the standard animation. No mismatch, no flash, animations preserved.

// Before: causes SSR/CSR mismatch
<motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} />

// After: safe during hydration, animates after mount
if (!isMounted) return <div className={className}>{children}</div>;
return <motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} />;

Noise suppression — beforeSend filter (silences #434, closes #435)

Added two beforeSend filters in instrumentation-client.ts for known browser-extension-only error patterns that cannot be fixed in app code:

Pattern Source Decision
Cannot assign to read only property 'pushState' Browser extensions wrapping history.pushState before app loads Suppress in Sentry
Object Not Found Matching Id:N, MethodName:update Chrome DevTools Protocol from extensions + CodeMirror Suppress in Sentry

Both patterns are well-documented browser extension artifacts. Neither is reproducible without the extension, neither has recurred since initial detection.

Issues resolved

GitHub Sentry Action
#434 GOSQLX-WEBSITE-4 (pushState) Silenced via beforeSend filter
#435 GOSQLX-WEBSITE-3 (Object Not Found) Closed — browser extension noise
#436 GOSQLX-WEBSITE-2 (Hydration JS error) Fixed via FadeIn.tsx
#437 GOSQLX-WEBSITE-1 (Hydration replay) Fixed (same root cause as #436)

Test plan

🤖 Generated with Claude Code

FadeIn.tsx — fix SSR hydration mismatch (Sentry #104333516, #104333514):
Framer Motion applies initial={{ opacity:0, y:16 }} before React hydrates,
causing the server-rendered DOM (opacity:1) to mismatch the client DOM.
Fix: render a plain <div> on server + during hydration (isMounted=false),
then swap to <motion.div> after useEffect fires. No mismatch, no flash.

instrumentation-client.ts — filter browser extension noise (Sentry #106232192, #105431404):
Add beforeSend hook to drop two known extension-only error patterns:
- "Cannot assign to read only property 'pushState'" — extensions wrap
  history.pushState before the app loads, making it read-only
- "Object Not Found Matching Id:N, MethodName:update" — Chrome DevTools
  Protocol messages from extensions interacting with CodeMirror

GitHub issue #435 (Object Not Found) closed as noise.
Issues #436/#437 (hydration) resolved by the FadeIn fix.
Issue #434 (pushState) silenced via beforeSend filter.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 26, 2026

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

Project Deployment Actions Updated (UTC)
gosqlx-website Ready Ready Preview, Comment Mar 26, 2026 7:25am

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 26, 2026

Claude finished @ajitpratap0's task —— View job


Code Review for PR #439

Todo List

  • Read and analyze FadeIn.tsx changes
  • Read and analyze instrumentation-client.ts changes
  • Review code quality and best practices
  • Check for potential bugs or issues
  • Assess performance considerations
  • Evaluate security concerns
  • Comment on test coverage
  • Provide comprehensive feedback

Review Summary

This is a well-executed fix that addresses real hydration issues while appropriately filtering browser extension noise. The solutions are technically sound and follow React SSR best practices.

Code Quality & Best Practices ✅

FadeIn.tsx (website/src/components/ui/FadeIn.tsx:7-20)

Excellent hydration fix implementation:

  • ✅ Proper use of useState(false) + useEffect pattern for client-only rendering
  • ✅ Clear, detailed comments explaining the hydration mismatch root cause
  • ✅ Maintains all existing functionality (animations, reduced motion, delays)
  • ✅ Clean separation between server/hydration render vs client render

instrumentation-client.ts (website/src/instrumentation-client.ts:15-35)

Professional error filtering:

  • ✅ Well-documented beforeSend filter with clear explanations
  • ✅ Specific error message matching (not overly broad)
  • ✅ Appropriate decision to suppress vs fix (unfixable external issues)

Potential Issues & Security 🟡

Minor Considerations

  1. Flash potential: There's a brief moment where <div> renders before <motion.div>, but this is unavoidable and better than hydration errors
  2. Error filtering scope: The current filters are appropriately narrow, but consider logging suppressed errors in development:
    if (process.env.NODE_ENV === "development") {
      console.warn("Suppressed Sentry error:", msg);
    }

Security Assessment ✅

  • No security concerns identified
  • Error filtering doesn't expose sensitive information
  • Sentry configuration follows security best practices

Performance Considerations ✅

FadeIn.tsx

  • Minimal overhead: Single useState + useEffect per component
  • No layout shifts: Server renders content immediately visible
  • Efficient re-renders: State change triggers single re-render to swap to motion.div

Sentry Filtering

  • Early filtering: beforeSend prevents network overhead for filtered errors
  • Fast string matching: Simple includes() checks are efficient

Test Coverage Recommendations 📝

Consider adding tests for:

  1. FadeIn component behavior:

    // Test server/hydration render
    expect(component.find('div').exists()).toBe(true);
    expect(component.find('motion.div').exists()).toBe(false);
    
    // Test post-mount render  
    await act(() => { /* trigger useEffect */ });
    expect(component.find('motion.div').exists()).toBe(true);
  2. Sentry beforeSend filter:

    // Test noise suppression
    const pushStateError = { exception: { values: [{ value: "Cannot assign to read only property 'pushState'" }] } };
    expect(beforeSend(pushStateError)).toBe(null);
    
    // Test legitimate errors pass through
    const realError = { exception: { values: [{ value: "Actual application error" }] } };
    expect(beforeSend(realError)).toBe(realError);

Additional Recommendations 🔧

FadeIn.tsx Enhancement

Consider memoizing the component to prevent unnecessary re-renders:

export const FadeIn = memo(function FadeIn({ children, delay = 0, className = '' }: Props) {
  // ... existing implementation
});

Monitoring Suggestion

Track suppressed error counts in development/staging to ensure filters aren't too aggressive:

if (process.env.NODE_ENV !== "production") {
  // Increment counter or log suppressed errors
}

Final Assessment

Recommendation: ✅ APPROVE

This PR effectively resolves real hydration issues with a clean, maintainable solution while appropriately filtering browser extension noise. The code quality is high, documentation is excellent, and the approach follows React SSR best practices.

Issues resolved: #434, #435, #436, #437
Risk level: Low - focused fixes with no breaking changes


- Wrap FadeIn in memo() to prevent unnecessary re-renders from parent
  state changes that don't affect the component's own props
- Log suppressed extension noise errors to console.debug in development
  so they remain visible locally while staying off Sentry in production

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 26, 2026

Claude finished @ajitpratap0's task —— View job


Code Review for PR #439

Todo List

  • Read and analyze FadeIn.tsx changes
  • Read and analyze instrumentation-client.ts changes
  • Review code quality and best practices
  • Check for potential bugs or issues
  • Assess performance considerations
  • Evaluate security concerns
  • Comment on test coverage
  • Provide comprehensive feedback

Review Summary

This is a well-executed fix that addresses real hydration issues while appropriately filtering browser extension noise. The solutions are technically sound and follow React SSR best practices.

Code Quality & Best Practices ✅

FadeIn.tsx (website/src/components/ui/FadeIn.tsx:7-20)

Excellent hydration fix implementation:

  • ✅ Proper use of useState(false) + useEffect pattern for client-only rendering
  • ✅ Clear, detailed comments explaining the hydration mismatch root cause
  • ✅ Maintains all existing functionality (animations, reduced motion, delays)
  • ✅ Clean separation between server/hydration render vs client render

instrumentation-client.ts (website/src/instrumentation-client.ts:15-35)

Professional error filtering:

  • ✅ Well-documented beforeSend filter with clear explanations
  • ✅ Specific error message matching (not overly broad)
  • ✅ Appropriate decision to suppress vs fix (unfixable external issues)

Potential Issues & Security 🟡

Minor Considerations

  1. Flash potential: There's a brief moment where <div> renders before <motion.div>, but this is unavoidable and better than hydration errors
  2. Error filtering scope: The current filters are appropriately narrow, but consider logging suppressed errors in development:
    if (process.env.NODE_ENV === "development") {
      console.warn("Suppressed Sentry error:", msg);
    }

Security Assessment ✅

  • No security concerns identified
  • Error filtering doesn't expose sensitive information
  • Sentry configuration follows security best practices

Performance Considerations ✅

FadeIn.tsx

  • Minimal overhead: Single useState + useEffect per component
  • No layout shifts: Server renders content immediately visible
  • Efficient re-renders: State change triggers single re-render to swap to motion.div

Sentry Filtering

  • Early filtering: beforeSend prevents network overhead for filtered errors
  • Fast string matching: Simple includes() checks are efficient

Test Coverage Recommendations 📝

Consider adding tests for:

  1. FadeIn component behavior:

    // Test server/hydration render
    expect(component.find('div').exists()).toBe(true);
    expect(component.find('motion.div').exists()).toBe(false);
    
    // Test post-mount render  
    await act(() => { /* trigger useEffect */ });
    expect(component.find('motion.div').exists()).toBe(true);
  2. Sentry beforeSend filter:

    // Test noise suppression
    const pushStateError = { exception: { values: [{ value: "Cannot assign to read only property 'pushState'" }] } };
    expect(beforeSend(pushStateError)).toBe(null);
    
    // Test legitimate errors pass through
    const realError = { exception: { values: [{ value: "Actual application error" }] } };
    expect(beforeSend(realError)).toBe(realError);

Additional Recommendations 🔧

FadeIn.tsx Enhancement

Consider memoizing the component to prevent unnecessary re-renders:

export const FadeIn = memo(function FadeIn({ children, delay = 0, className = '' }: Props) {
  // ... existing implementation
});

Monitoring Suggestion

Track suppressed error counts in development/staging to ensure filters aren't too aggressive:

if (process.env.NODE_ENV !== "production") {
  // Increment counter or log suppressed errors
}

Final Assessment

Recommendation: ✅ APPROVE

This PR effectively resolves real hydration issues with a clean, maintainable solution while appropriately filtering browser extension noise. The code quality is high, documentation is excellent, and the approach follows React SSR best practices.

Issues resolved: #434, #435, #436, #437
Risk level: Low - focused fixes with no breaking changes


@ajitpratap0 ajitpratap0 merged commit 791b4a6 into main Mar 26, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant