Skip to content

Fix/Socket reconnection rehydrates#5966

Closed
ParagGhatage wants to merge 3 commits intoreflex-dev:mainfrom
ParagGhatage:fix/socket-reconnect
Closed

Fix/Socket reconnection rehydrates#5966
ParagGhatage wants to merge 3 commits intoreflex-dev:mainfrom
ParagGhatage:fix/socket-reconnect

Conversation

@ParagGhatage
Copy link
Copy Markdown

All Submissions:

  • Have you followed the guidelines stated in CONTRIBUTING.md file?
  • Have you checked to ensure there aren't any other open Pull Requests for the desired changed?

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Changes To Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you successfully ran tests with your changes locally?

After these steps, you're ready to open a pull request.

Fix: Reload frontend on socket reconnect when backend state has expired

Description

This PR fixes the issue where the frontend retains stale state when the backend state has expired, and the socket reconnects silently.

Problem

Previously, the backend only emitted a reload event when an incoming frontend event (e.g., a click) triggered state access.
When the socket reconnected automatically - without any user interaction - the stale frontend state remained until the next user action caused rehydration.

Fix

  • Extended on_connect() in app.py to check the backend state immediately after reconnect.
  • If the state corresponding to the client token no longer exists or has an empty router_data, a "reload" event is emitted instantly to the client.
  • This ensures the frontend rehydrates as soon as it reconnects, keeping UI and backend state in sync.

Behavior Summary

✅ Detects expired backend state right after reconnect.
✅ Emits "reload" to prompt frontend rehydration.
✅ Prevents users from seeing stale UI after backend restarts or token expiry.

Linked Issue

Closes #5963

…nects and reconnects by checking backend state still exists or is expired just after reconnect
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Nov 10, 2025

Greptile Overview

Greptile Summary

Adds logic to detect expired backend state on socket reconnection and emit a reload event to the frontend.

Key changes:

  • Extended on_connect() to check if backend state has expired after reconnection
  • Attempts to emit a reload event when router_data is empty, indicating expired state

Critical issue found:

  • The check for expired state occurs AFTER link_token_to_sid() has already populated router_data[SESSION_ID] on line 2089
  • By the time the check runs on line 2097, router_data is no longer empty, so the condition will never be True
  • This means the reload will never be emitted, and the intended fix will not work

Confidence Score: 0/5

  • This PR contains a critical logic bug that prevents the intended fix from working
  • The check for expired state occurs after link_token_to_sid has already modified router_data, making the condition always False. The reload event will never be emitted, so the PR does not actually fix the reported issue.
  • reflex/app.py requires immediate attention - the logic order must be corrected

Important Files Changed

File Analysis

Filename Score Overview
reflex/app.py 1/5 Added logic to detect expired state on reconnection, but the check occurs after state is already modified - the reload will never trigger

Sequence Diagram

sequenceDiagram
    participant Frontend
    participant Socket
    participant on_connect
    participant link_token_to_sid
    participant StateManager
    participant State
    
    Frontend->>Socket: Reconnect with token
    Socket->>on_connect: on_connect(sid, environ)
    on_connect->>link_token_to_sid: link_token_to_sid(sid, token)
    link_token_to_sid->>StateManager: modify_state(token)
    StateManager->>State: get or create state
    link_token_to_sid->>State: router_data[SESSION_ID] = sid
    State-->>link_token_to_sid: state modified
    link_token_to_sid-->>on_connect: completed
    
    Note over on_connect,State: BUG: Check happens here AFTER router_data is populated
    on_connect->>StateManager: get_state(token)
    StateManager-->>on_connect: state (with router_data={SESSION_ID: sid})
    on_connect->>State: check if not router_data
    State-->>on_connect: False (router_data is NOT empty)
    Note over on_connect: Reload never emitted!
    on_connect-->>Socket: connection complete
Loading

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment thread reflex/app.py Outdated
Comment on lines +2093 to +2101
try:
if token:
substate_key = _substate_key(token, self.app.state_manager.state)
state = await self.app.state_manager.get_state(substate_key)
if not getattr(state, "router_data", None):
console.debug(
f"[Reflex] socket reconnect detected with expired token {token}, emitting reload"
)
await self.emit("reload", data={"reason": "state expired"}, to=sid)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

logic: logic bug: check for expired state occurs AFTER link_token_to_sid already populated router_data[SESSION_ID] (line 2089 calls this, which sets router_data at line 2287). By the time this check runs, router_data is {SESSION_ID: sid}, so not getattr(state, "router_data", None) is always False. The reload will never be emitted.

move this check BEFORE line 2089, or check if router_data was empty before link_token_to_sid modified it

Prompt To Fix With AI
This is a comment left during a code review.
Path: reflex/app.py
Line: 2093:2101

Comment:
**logic:** logic bug: check for expired state occurs AFTER `link_token_to_sid` already populated `router_data[SESSION_ID]` (line 2089 calls this, which sets `router_data` at line 2287). By the time this check runs, `router_data` is `{SESSION_ID: sid}`, so `not getattr(state, "router_data", None)` is always `False`. The reload will never be emitted.

move this check BEFORE line 2089, or check if `router_data` was empty before `link_token_to_sid` modified it

How can I resolve this? If you propose a fix, please make it concise.

@ParagGhatage
Copy link
Copy Markdown
Author

@masenf ,
I have rectified the logical error mentioned by greptile in the second commit.
Please take a look.

Copy link
Copy Markdown
Collaborator

@masenf masenf left a comment

Choose a reason for hiding this comment

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

this doesn't work for me at all. it tries to send "reload" on the initial connection with an invalid payload.

did you test this? did it work for you?

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Nov 11, 2025

CodSpeed Performance Report

Merging #5966 will not alter performance

Comparing ParagGhatage:fix/socket-reconnect (1590fa4) with main (3c3ddc2)

Summary

✅ 8 untouched

@ParagGhatage
Copy link
Copy Markdown
Author

this doesn't work for me at all. it tries to send "reload" on the initial connection with an invalid payload.

did you test this? did it work for you?

@masenf,
Sorry about the earlier code breaking - I realize now that the "reload" was being triggered even on the initial connection before the socket was fully established.
I’ve updated the logic in my latest commit to ensure the reload only fires when a reconnection is detected and the backend state has actually expired.

Also saw your PR #5969 - makes total sense to move this logic to the frontend and handle rehydration directly through the backend instead of a full reload.
That approach looks much cleaner.

@adhami3310 adhami3310 closed this Nov 13, 2025
@ParagGhatage ParagGhatage deleted the fix/socket-reconnect branch November 14, 2025 09:50
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.

When socket reconnects, and state is expired, it should rehydrate

3 participants