Skip to content

Fix the desktop window flashing endlessly after a flet run hot reload - #6945

Open
ndonkoHenri wants to merge 4 commits into
flet-1.1.0from
fix/socket-single-disconnect-6913
Open

ndonkoHenri wants to merge 4 commits into
flet-1.1.0from
fix/socket-single-disconnect-6913

Conversation

@ndonkoHenri

@ndonkoHenri ndonkoHenri commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #6913

The bug

Under flet run, the desktop client stays open across hot reloads. Each reload kills the Python app and the client reconnects to the new one. When the app is killed while the client is sending to it (window events, control events, method replies), the client's socket gets a reset: WSAECONNRESET on Windows, ECONNRESET or EPIPE on Linux. The chain from there:

  1. For a reset, Dart's socket stream fires onError and then onDone. FletSocketBackendChannel called onDisconnect() from both.
  2. FletBackend._onDisconnect() schedules a reconnect for every call. So one reset started two independent reconnect chains.
  3. The Python socket server replaces the active connection with the newest one. The two chains kept kicking each other off: each kicked chain reconnected 200 ms later and created a new session, with a loading screen and a full re-render each time.

That is the endless flashing in the report. The log shows App session started / Session was garbage collected every few hundred ms from a single app process, until flet run is restarted. It needs a reset at the exact moment of a reload, which is why it starts "randomly after a few minutes". An orderly close (FIN) fires only onDone.

The fix

  • FletSocketBackendChannel destroys the socket on either event but reports the disconnect only once. Each connection attempt gets a new channel, so later disconnects are still reported.
  • FletBackend now keeps at most one reconnect pending, and only the channel of the latest connection attempt may trigger one. This makes the backend safe whatever a transport does: an embedder-supplied channel, a future transport, or a late event from a replaced channel. On its own, without the channel change, it also reconnects exactly once after a reset.

Testing

  • Windows 11 ARM64 VM, real desktop client: reloads with client traffic in flight churned in 4 of 5 runs before the fix, and in 0 of 4 with it.
  • Ubuntu 24.04 aarch64 VM, clients built from flet-1.1.0 and from this branch:
    • Before the fix, both the released 1.0.4 client and an unfixed build showed the reporter's pattern: 40 to 56 sessions in about 2.5 s from one app process, with the window stuck on "Working...". On Linux the churn also hung hot reload.
    • With the fix: 81 reloads with the same traffic, exactly 1 session per app process, no hang.
  • Dart tests, added in packages/flet/test/transport/:
    • socket_disconnect_test.dart runs the socket channel and FletBackend against a local TCP server. A connection reset and an orderly close each report one disconnect, and the backend reconnects exactly once after a reset. On flet-1.1.0 the reset cases fail: 2 disconnect reports instead of 1, and 360 connections instead of 2.
    • backend_reconnect_test.dart uses an injected channel. A disconnect reported twice by a channel reconnects once, and a late disconnect from a replaced channel doesn't reconnect, while the current channel still does. Both fail without the backend guard, even with the socket fix in place.
    • The packages/flet suite passes, and flutter analyze is clean.

The VM runs above used the socket fix; the backend guard is covered by the Dart tests.

Summary by Sourcery

Prevent competing reconnect attempts from repeatedly replacing desktop sessions after hot reload connection resets.

Bug Fixes:

  • Prevent desktop windows from flashing endlessly after flet run hot reloads by ensuring connection resets produce only one valid reconnect.

Enhancements:

  • Make backend reconnect handling resilient to duplicate disconnect events and stale connection callbacks.

Tests:

  • Add coverage for duplicate disconnect reports, stale channels, TCP resets, orderly socket closes, and single-reconnect behavior.

A connection reset or a broken pipe fires the socket stream's onError
and then onDone, and both reported a disconnect. FletBackend then ran
two reconnect chains that kept replacing each other's connection, so
after a hot reload the desktop window flashed with a new session every
few hundred milliseconds. Seen on Windows and Linux when the client was
sending events while the app was restarted.
FletBackend scheduled a reconnect for every disconnect a channel
reported, so any transport that reported a disconnect twice started two
reconnect chains that kept replacing each other's connection. Only let
the channel of the latest connection attempt trigger a reconnect, and
schedule at most one reconnect at a time, so the backend is safe
whatever the transport does.

Checked with the socket reset test: without the channel fix from the
previous commit, the backend now still reconnects exactly once.
@ndonkoHenri
ndonkoHenri requested review from FeodorFitsner and a balanced review from Copilot October 10, 2026 18:38

@sourcery-ai sourcery-ai Bot left a comment

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.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Copilot AI left a comment

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.

🟡 Changes recommended

The socket and backend regression tests described in the PR are not committed, leaving both concurrency guarantees unprotected.

2 open findings
What changed in this PR

Prevents competing reconnect loops that caused desktop flashing after hot reloads.

Changes:

  • Makes socket disconnect notification idempotent.
  • Rejects stale disconnect callbacks and duplicate reconnect scheduling.
  • Adds user-facing and Flutter-package changelog entries.
File Description
packages/​flet/​lib/​src/​transport/​flet_backend_channel_socket.dart Reports each socket disconnect once.
packages/​flet/​lib/​src/​flet_backend.dart Guards reconnect scheduling and stale channels.
packages/​flet/​CHANGELOG.md Documents the Dart runtime fix.
CHANGELOG.md Documents the user-facing hot-reload fix.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/flet/lib/src/flet_backend.dart
Comment thread packages/flet/lib/src/transport/flet_backend_channel_socket.dart
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Deploying flet-website-v2 with  Cloudflare Pages  Cloudflare Pages

Latest commit: c2cf7be
Status: ✅  Deploy successful!
Preview URL: https://d0b9de69.flet-website-v2.pages.dev
Branch Preview URL: https://fix-socket-single-disconnect.flet-website-v2.pages.dev

View logs

socket_disconnect_test.dart runs FletSocketBackendChannel and FletBackend
against a local TCP server: a connection reset and an orderly close each
report one disconnect, and the backend reconnects exactly once after a
reset. backend_reconnect_test.dart uses an injected channel: a disconnect
reported twice reconnects once, and a late disconnect from a replaced
channel doesn't reconnect while the current channel still does.

Each test fails without its part of the fix.
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.

bug: While using flet run, the window starts flashing continuously after few minutes of testing changes.

2 participants