Skip to content

fix: resolve setup wizard flashing on auth screen#1888

Open
AndyMik90 wants to merge 2 commits intodevelopfrom
fix/setup-wizard-flashing-1882
Open

fix: resolve setup wizard flashing on auth screen#1888
AndyMik90 wants to merge 2 commits intodevelopfrom
fix/setup-wizard-flashing-1882

Conversation

@AndyMik90
Copy link
Owner

@AndyMik90 AndyMik90 commented Feb 22, 2026

Summary

  • Fixes the setup wizard screen flashing/flickering constantly when navigating to the "Configure Claude Authentication" step
  • Root cause: loadClaudeProfiles in OAuthStep.tsx was defined as a plain async function, creating a new reference every render. Since it was a useEffect dependency, this caused an infinite re-render loop (effect fires → state update → re-render → new function ref → effect fires again)
  • Fix: Wrapped loadClaudeProfiles in useCallback with [] deps to stabilize the function reference

Fixes #1882

Test plan

  • All 3467 frontend unit tests pass
  • Launch app fresh → Setup Wizard → "Sign in with Anthropic" → auth screen should render stably without flashing

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor
    • Improved profile-loading logic to stabilize behavior across renders and reduce unnecessary updates.
  • Bug Fixes / Privacy
    • Adjusted authentication logging to avoid exposing email addresses in logs.

The OAuthStep component had an infinite re-render loop caused by
loadClaudeProfiles being defined as a plain async function without
useCallback. On every render, a new function reference was created,
which triggered the useEffect (which listed it as a dependency),
which called setIsLoadingProfiles(true) causing another re-render,
creating an endless cycle that manifested as screen flashing.

Fix: wrap loadClaudeProfiles in useCallback with an empty dependency
array so the function reference is stable across renders.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@github-actions github-actions bot added area/frontend This is frontend only bug Something isn't working size/XS Extra small (< 10 lines) labels Feb 22, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @AndyMik90, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical UI bug in the setup wizard where the Claude authentication screen experienced persistent flickering. The fix involves optimizing a function's reference stability to prevent an unintended infinite re-render cycle, ensuring a smooth user experience during the onboarding process.

Highlights

  • Fixes UI Flashing: Resolved an issue where the setup wizard's 'Configure Claude Authentication' screen would flash or flicker constantly.
  • Root Cause Identified: The flashing was caused by an infinite re-render loop due to the loadClaudeProfiles function being redefined on every render, acting as a useEffect dependency.
  • Stabilized Function Reference: The loadClaudeProfiles function was wrapped in useCallback with an empty dependency array ([]) to ensure its reference remains stable across renders, thus breaking the re-render loop.
Changelog
  • apps/frontend/src/renderer/components/onboarding/OAuthStep.tsx
    • Wrapped loadClaudeProfiles in useCallback to stabilize its reference.
    • Added an empty dependency array to useCallback for loadClaudeProfiles.
Activity
  • All 3467 frontend unit tests passed.
  • A manual test plan was outlined to verify the fix by launching the app fresh and navigating to the authentication screen.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 22, 2026

📝 Walkthrough

Walkthrough

loadClaudeProfiles in the OAuthStep component was converted to a memoized callback via useCallback and the mounting useEffect now depends on that memoized function; a log in handleAuthTerminalSuccess was altered to omit the email value from output. (45 words)

Changes

Cohort / File(s) Summary
OAuthStep updates
apps/frontend/src/renderer/components/onboarding/OAuthStep.tsx
Replaced plain async loadClaudeProfiles with const loadClaudeProfiles = useCallback(async () => { ... }, []) and updated the useEffect to depend on it; adjusted handleAuthTerminalSuccess log to hide the email parameter.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Suggested labels

size/S

Poem

🐰 I hopped through hooks with nimble feet,
Wrapped a loader so renders meet,
Silence now the flashing light,
Hidden emails tucked from sight,
A tiny change — the setup's neat! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely describes the main fix: resolving the setup wizard flashing issue on the auth screen, which is the primary change in this pull request.
Linked Issues check ✅ Passed The pull request fully addresses issue #1882 by fixing the root cause of the setup wizard flashing through memoization of loadClaudeProfiles with useCallback, stabilizing the function reference and preventing the infinite re-render loop.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the flashing issue (#1882) and addressing a GDPR/CCPA concern by removing PII from logs, both within the stated objectives.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/setup-wizard-flashing-1882

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively resolves an infinite re-render loop in the setup wizard's authentication screen. The root cause was correctly identified as an async function being redefined on each render and used as a useEffect dependency. Wrapping loadClaudeProfiles in useCallback with an empty dependency array is the correct solution to memoize the function and provide a stable reference, thus breaking the re-render cycle. The change is well-implemented and directly addresses the reported issue.

@AndyMik90 AndyMik90 self-assigned this Feb 22, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/frontend/src/renderer/components/onboarding/OAuthStep.tsx (1)

233-242: ⚠️ Potential issue | 🟠 Major

console.warn logs a raw email address (PII) to the console.

In Electron, console output is typically captured in persistent log files (e.g., via electron-log or the default Chromium log). Logging email here means a user's email address can end up in on-disk logs, which is a GDPR/CCPA compliance risk.

🛡️ Proposed fix — redact or drop the PII value
 const handleAuthTerminalSuccess = useCallback(async (email?: string) => {
-  console.warn('[OAuthStep] Auth success:', email);
+  console.warn('[OAuthStep] Auth success');  // omit email to avoid PII in logs
   // Close terminal immediately

Or, if the email is genuinely needed for diagnostics, route it to Sentry with appropriate PII scrubbing configured, rather than the console.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/frontend/src/renderer/components/onboarding/OAuthStep.tsx` around lines
233 - 242, The console.warn in handleAuthTerminalSuccess currently logs the raw
email (PII) which may be persisted in Electron logs; remove the direct logging
of the email or redact it (e.g., replace with "<redacted-email>") before
logging, or instead send any needed diagnostic info to your error/telemetry
system (Sentry) with PII scrubbing enabled; update the function that references
handleAuthTerminalSuccess (and keep the existing setAuthTerminal,
setAuthenticatingProfileId, loadClaudeProfiles calls) so no raw email value is
written to console output.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@apps/frontend/src/renderer/components/onboarding/OAuthStep.tsx`:
- Around line 233-242: The console.warn in handleAuthTerminalSuccess currently
logs the raw email (PII) which may be persisted in Electron logs; remove the
direct logging of the email or redact it (e.g., replace with "<redacted-email>")
before logging, or instead send any needed diagnostic info to your
error/telemetry system (Sentry) with PII scrubbing enabled; update the function
that references handleAuthTerminalSuccess (and keep the existing
setAuthTerminal, setAuthenticatingProfileId, loadClaudeProfiles calls) so no raw
email value is written to console output.

Copy link
Owner Author

@AndyMik90 AndyMik90 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Auto Claude PR Review

PR #1888 reviewed: 6 findings (0 structural issues). Verdict: approve.

Findings (6 selected of 6 total)

🟡 [QLT-1] [MEDIUM] useCallback with empty deps closes over stale state/props

📁 apps/frontend/src/renderer/components/onboarding/OAuthStep.tsx:82

The useCallback wrapping loadClaudeProfiles uses an empty dependency array [], but the function body calls setClaudeProfiles, setError, and setIsLoadingProfiles — which are stable setState references and therefore safe — but it also likely references other variables from the component scope (e.g., any values used inside the try/catch that aren't shown in the diff). With [] deps, if the function ever needs to read props or non-setState closures, it will capture stale values. While this fixes the infinite loop, the correct long-term fix would be to audit the full function body and list any non-setState dependencies, or move the async logic outside the component / into a custom hook.

Suggested fix:

Audit the full body of `loadClaudeProfiles` for any references to props or state variables. If none exist beyond setState calls, `[]` is correct. If any exist, add them to the dependency array. Consider adding an ESLint `react-hooks/exhaustive-deps` check to validate.

🔵 [QLT-2] [LOW] Generic error handling swallows error details

📁 apps/frontend/src/renderer/components/onboarding/OAuthStep.tsx:90

The catch block (lines ~90-94, partially visible) sets an error state but it's unclear whether it logs the actual exception or preserves its type. If the catch simply does setError(someGenericMessage) without logging, debugging production issues will be difficult. This is a pre-existing issue but worth flagging since the function is being refactored.

Suggested fix:

Ensure the catch block logs the original error (e.g., `console.error('Failed to load Claude profiles:', err)`) in addition to setting user-facing error state.

🔵 [QLT-3] [LOW] Missing test coverage noted in PR checklist

📁 apps/frontend/src/renderer/components/onboarding/OAuthStep.tsx:99

The test plan checkbox for manual verification ('Launch app fresh → Setup Wizard → auth screen should render stably without flashing') is unchecked. Given that this bug is a user-visible infinite re-render loop, it would be valuable to add a unit/integration test that asserts loadClaudeProfiles is only called once on mount (e.g., by mocking the API and asserting call count), preventing regression.

Suggested fix:

Add a test that renders <OAuthStep />, mocks the profile-loading API, and asserts it is called exactly once, not in a loop.

🟡 [DEEP-1] [MEDIUM] useCallback with empty deps captures stale closure over state setters only - safe but potentially fragile

📁 apps/frontend/src/renderer/components/onboarding/OAuthStep.tsx:82

The useCallback with [] dependencies is safe for React state setters (which are stable), but the function body at lines 80-96 may reference other variables from the component scope that could become stale. Without seeing the full component, if loadClaudeProfiles ever needs to reference props or other state values (not just setters), the empty dependency array would cause stale closure bugs. However, based on the visible code, this appears safe for now since it only calls setters and presumably stable API functions.

Suggested fix:

Verify that the function body only references stable values (React state setters, module-level imports). If it ever needs to read props or state values, add them to the useCallback dependency array. Consider using useRef for values that need to be read without triggering re-creation.

🟡 [DEEP-2] [MEDIUM] Missing cleanup/cancellation for async effect - potential state update on unmounted component

📁 apps/frontend/src/renderer/components/onboarding/OAuthStep.tsx:82

The loadClaudeProfiles function performs async work and then calls setIsLoadingProfiles(false) in a finally block. If the component unmounts while the async operation is in-flight (e.g., user navigates away via onBack/onSkip during loading), state updates will be attempted on an unmounted component. While React 18+ suppresses the warning, this is still a logic issue that could cause unexpected behavior - for example, if the component remounts, the stale promise resolution could interfere with a fresh load.

Suggested fix:

Add an AbortController or a cleanup flag via useEffect's cleanup function to cancel or ignore the result of the async operation when the component unmounts. For example: useEffect(() => { let cancelled = false; loadClaudeProfiles(cancelled); return () => { cancelled = true; }; }, [loadClaudeProfiles]);

🔵 [DEEP-3] [LOW] Race condition if loadClaudeProfiles is called multiple times concurrently

📁 apps/frontend/src/renderer/components/onboarding/OAuthStep.tsx:82

The function is described as 'reusable' and is both called on mount (via useEffect) and presumably callable from other UI interactions. If invoked concurrently (e.g., rapid user interaction plus mount effect), two concurrent executions would both set setIsLoadingProfiles(true), and the first to complete would set it to false while the second is still in-flight, causing incorrect loading state. The results from the first call could also briefly flash before being overwritten by the second.

Suggested fix:

Add a guard (e.g., check `isLoadingProfiles` before proceeding, or use an AbortController to cancel previous in-flight requests) to prevent concurrent executions from creating inconsistent state.

This review was generated by Auto Claude.

Remove raw email address from console.warn output to prevent PII
from being written to persistent Electron log files, addressing
GDPR/CCPA risk.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/frontend/src/renderer/components/onboarding/OAuthStep.tsx (1)

233-242: ⚠️ Potential issue | 🟡 Minor

Remove the unused email parameter from handleAuthTerminalSuccess.

The parameter is never referenced in the function body and can be safely removed since the onAuthSuccess prop type in AuthTerminal defines it as optional.

♻️ Proposed fix
-  const handleAuthTerminalSuccess = useCallback(async (email?: string) => {
+  const handleAuthTerminalSuccess = useCallback(async () => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/frontend/src/renderer/components/onboarding/OAuthStep.tsx` around lines
233 - 242, Remove the unused email parameter from the handleAuthTerminalSuccess
callback: update the function signature in the component so it no longer accepts
the unused email argument (handleAuthTerminalSuccess) and ensure any place
passing it (e.g., AuthTerminal's onAuthSuccess) still works since the prop
defines the parameter as optional; keep the existing body (resetting
setAuthTerminal, setAuthenticatingProfileId, and awaiting loadClaudeProfiles)
and update the useCallback dependency array as needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@apps/frontend/src/renderer/components/onboarding/OAuthStep.tsx`:
- Around line 233-242: Remove the unused email parameter from the
handleAuthTerminalSuccess callback: update the function signature in the
component so it no longer accepts the unused email argument
(handleAuthTerminalSuccess) and ensure any place passing it (e.g.,
AuthTerminal's onAuthSuccess) still works since the prop defines the parameter
as optional; keep the existing body (resetting setAuthTerminal,
setAuthenticatingProfileId, and awaiting loadClaudeProfiles) and update the
useCallback dependency array as needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/frontend This is frontend only bug Something isn't working size/XS Extra small (< 10 lines)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Setup wizard fails at the beginning

1 participant