[Bug]: Closing a server preview tab never fires beforeunload or pagehide#23513

opened20d agobyDDavidMendesOS

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. Serve a local page that reports its unload events:
    html
    1<button>A</button>
    2<script>
    3 for (const type of ["beforeunload", "pagehide", "visibilitychange"])
    4 addEventListener(type, () => navigator.sendBeacon(`/beacon?${type}`));
    5</script>
  2. preview_open it, and optionally preview_click the button (user activation).
  3. t3_preview_close the tab.
  4. Check the server log for beacons.

Expected behavior

Closing the tab runs the page's unload lifecycle, as closing a tab in a normal browser does, so pages that save or report state on pagehide/beforeunload behave the same under agent testing.

Actual behavior

No beacon arrives for any of the three events, with or without the prior click. dropTab (apps/server/src/preview/ServerBrowser.ts:675 on 680152e27b) calls tab.page.close(). Playwright's default is runBeforeUnload: false, which closes the page without running unload handlers.

Impact

Minor bug or occasional failure

Version or commit

T3 Code (Beta) 0.1.1 desktop; code reference from main @ 680152e27b

Environment

macOS 26.6.2, server-side preview (Playwright engine)

Workaround

None found.

Notes on a fix

page.close({ runBeforeUnload: true }) alone is not enough. With it, a page whose beforeunload handler calls preventDefault() raises a beforeunload dialog and stays open until that dialog is answered. T3 only records dialogs for preview_dialog, so the close would hang on a dialog no one answers. The close path would also need to accept that dialog (or decide not to). That changes what closing a tab means for pages with unsaved-changes guards, so I'd like to know the intended behaviour before proposing a PR:

  • Should closing a tab (agent tool, Browser panel, idle close) run unload handlers at all?
  • If so, should a beforeunload prompt during close be accepted automatically, or reported back to the caller like other dialogs?

Filed by Claude Code (claude-opus-5-5) for @DavidMendesOS.

Note

🤖 Claude Opus 5.5 reported on behalf of NikitaMGrimm

Another reproduction on the desktop path, where the impact is losing a site's login.

Environment: Windows 11, T3 Code 0.1.7-nightly.20261102 (my fork's build; same tab.desktop.close() close path as main at b7ab02e0f607), serverPageMode: native, default profile.

Test on example.com: I registered beforeunload, pagehide, unload and visibilitychange listeners that each write a localStorage key.

ActionKeys written by the listenersKey set before the action
Navigate the tabAll fourn/a
t3_preview_close, then reopenNoneStill there

So the events either don't fire or their writes never land. I didn't test closing a tab by hand from the UI.

Impact: a signed-in Discord tab stayed signed in across navigation. After t3_preview_close, reopening Discord showed "Please log in again": the token localStorage key was gone, while the account's entry in MultiAccountStore remained. I believe Discord writes its token back during unload, but I haven't confirmed that.

This matters for agents in particular, because they're told to close the tabs they open, and the close reports success. The user finds out at their next visit.

#23537 only changes the headless branch, so this path stays as it is. Should desktop tabs run unload handlers on close too?