Skip to content

macos: cancel deferred tab presentation on close - #12119

Merged
mitchellh merged 1 commit into
ghostty-org:mainfrom
jamylak:fix/macos-ghost-tab
Apr 7, 2026
Merged

mitchellh merged 1 commit into
ghostty-org:mainfrom
jamylak:fix/macos-ghost-tab

Conversation

@jamylak

@jamylak jamylak commented Apr 5, 2026 •

Copy link
Copy Markdown
Contributor

The 👻 Ghost Tab Issue

ghost_tab_720p.mov

Previous failure scenario (User perspective):

  1. Open a new tab
  2. Instantly trigger close other tabs (eg. through custom user keyboard shortcut)
  3. Now you will see an empty Ghost Tab (Only a window bar with empty content)

The previous failure mode is:

  1. Create a tab or window now in newTab(...) / newWindow(...).
  2. Queue its initial show/focus work with DispatchQueue.main.async.
  3. Close that tab or window with closeTabImmediately() / closeWindowImmediately() before the queued callback runs.
  4. The queued callback still runs anyway and calls showWindow(...) / makeKeyAndOrderFront(...) on stale state.
  5. The tab can be resurrected as a half-closed blank ghost tab.

The fix:

  • Store deferred presentation work in a cancellable DispatchWorkItem and cancel it from the close paths before AppKit finishes tearing down the tab or window.
  • This prevents the stale show/focus callback from running after close.

AI Usage

I used GPT 5.4 to find the initial issue and fix it. I cleaned up and narrowed down the commit afterwards.


Additional Notes:

I use cmd+o to close_tab:other
https://github.com/jamylak/dotfiles/blob/main/ghostty/config#L106C1-L106C34

Try it for your self if you want to reproduce, just do a quick cmd+t cmd+o and you will see

The 👻 Ghost Tab Issue

Previous failure scenario (User perspective):

1. Open a new tab
2. Instantly trigger close other tabs
   (eg. through custom user keyboard shortcut)
3. Now you will see an empty Ghost Tab
   (Only a window bar with empty content)

The previous failure mode is:

1. Create a tab or window now in `newTab(...)` / `newWindow(...)`.
2. Queue its initial show/focus work with `DispatchQueue.main.async`.
3. Close that tab or window with `closeTabImmediately()` /
 `closeWindowImmediately()` before the queued callback runs.
4. The queued callback still runs anyway and calls `showWindow(...)` /
 `makeKeyAndOrderFront(...)` on stale state.
5. The tab can be resurrected as a half-closed blank ghost tab.

The fix:

- Store deferred presentation work in a cancellable
  DispatchWorkItem and cancel it from the close paths
  before AppKit finishes tearing down the tab or window.
- This prevents the stale show/focus callback from
  running after close.
@ghostty-bot ghostty-bot Bot added the os/macos label Apr 5, 2026
@ghostty-vouch

ghostty-vouch Bot commented Apr 5, 2026

Copy link
Copy Markdown
Contributor

Hi @jamylak, thanks for your interest in contributing!

This project requires that pull request authors are vouched, and you are not in the list of vouched users.

This PR will be closed automatically. See https://github.com/ghostty-org/ghostty/blob/main/CONTRIBUTING.md for more details.

@ghostty-vouch ghostty-vouch Bot closed this Apr 5, 2026
@bo2themax

Copy link
Copy Markdown
Member

!vouch

ghostty-vouch Bot pushed a commit that referenced this pull request Apr 5, 2026
ghostty-vouch Bot added a commit that referenced this pull request Apr 5, 2026
Triggered by
[comment](#12119 (comment))
from @bo2themax.

Vouch: @jamylak

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@ghostty-vouch ghostty-vouch Bot mentioned this pull request Apr 5, 2026
@bo2themax

Copy link
Copy Markdown
Member

I did encounter this once, but I couldn't reproduce it after. The fix seems reasonable. I'll reopen it first and try this later

@bo2themax bo2themax reopened this Apr 5, 2026
@bo2themax
bo2themax marked this pull request as ready for review April 5, 2026 10:42
@bo2themax
bo2themax requested a review from a team as a code owner April 5, 2026 10:42
@jamylak

jamylak commented Apr 6, 2026

Copy link
Copy Markdown
Contributor Author

I did encounter this once, but I couldn't reproduce it after. The fix seems reasonable. I'll reopen it first and try this later

thanks for that

@mitchellh mitchellh added this to the 1.3.2 milestone Apr 6, 2026
@mitchellh
mitchellh merged commit 0043e66 into ghostty-org:main Apr 7, 2026
89 checks passed
bo2themax added a commit to bo2themax/ghostty that referenced this pull request Apr 11, 2026
Regression of ghostty-org#12119, this memory leak affects new tabs, since the terminal controller is not deallocated correctly. Hitting `cmd+t` will create a new window with two tabs, but only one actually contains usable surface.

You can reproduce by:
1. Quit and Reopen Ghostty
2. Open a new window if no window is created (initial-window = false)
3. Close the window
4. Hit `cmd+t`
mitchellh added a commit that referenced this pull request Apr 11, 2026
Regression of #12119, this memory leak affects new tabs, since the
terminal controller is not deallocated correctly, hitting `cmd+t` will
create a new window with two tabs, but only one actually contains usable
surface.

You can reproduce by:
1. Quit and Reopen Ghostty
2. Open a new window if no window is created (initial-window = false)
3. Close the window
4. Hit `cmd+t`
Jesssullivan pushed a commit to Jesssullivan/ghostty that referenced this pull request Apr 13, 2026
christmic pushed a commit to christmic/Sylvander that referenced this pull request Aug 12, 2026
Triggered by
[comment](ghostty-org/ghostty#12119 (comment))
from @bo2themax.

Vouch: @jamylak

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@mitchellh mitchellh modified the milestones: 1.3.2, 1.4.0 Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants