#3192

macOS: intermittent NSRangeException from _NSTouchBarFinderObservation removing a nextResponder observer

Openh-ricaopened 1mo ago

An application built on gpui-kit intermittently aborts on macOS with an uncaught AppKit exception raised by the Touch Bar finder. No application frames are involved: the throw happens inside AppKit during a display-cycle flush on the main run loop.

Environment

  • gpui-kit git revision 86f9c0fb5907549d9a5b0aff53b4b6865933a8e8 (GPUI gpui-pre =0.3.6)
  • MacBookPro17,1 (M1, with Touch Bar), macOS 26.6.2
  • Window opened with gpui_kit::open_window and TitleBar::window_options(), a TitleBar in the view, default key bindings and an app menu

Exception

*** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <_NSTouchBarFinderObservation 0x…> for the key path "nextResponder" from <NSView 0x…> because it is not registered as an observer.'

Top of the exception backtrace:

Foundation  -[NSObject(NSKeyValueObserverRegistration) _removeObserver:forProperty:]
Foundation  -[NSObject(NSKeyValueObserverRegistration) removeObserver:forKeyPath:]
Foundation  -[NSObject(NSKeyValueObserverRegistration) removeObserver:forKeyPath:context:]
AppKit      -[_NSTouchBarFinderObservation invalidate]
AppKit      ___NSTouchBarFinderSetNeedsUpdateOnMain_block_invoke_2
AppKit      NSDisplayCycleObserverInvoke
AppKit      NSDisplayCycleFlush
QuartzCore  CA::Transaction::run_commit_handlers(CATransactionPhase)
QuartzCore  CA::Transaction::commit()
...
AppKit      -[NSApplication run]
<app>       gpui_macos::platform::MacPlatform::run

Reproduction

Rare and timing dependent. It occurred:

  1. once during normal use of the app, about four minutes after launch, while focus was moving between two instances of the same app;
  2. once in a single instance while programmatically switching the frontmost app between it and Finder (Accessibility AXFrontmost), within the first 25 switches.

A further ~320 switches did not reproduce it, neither in the app nor in a minimal gpui_kit::open_window window, so I cannot yet say whether anything specific to the app raises the rate.

Possibly related

  • feigeCode/navop#268 — a GPUI app hitting the same _NSTouchBarFinderObservation exception on an Intel Touch Bar Mac when closing a window; worked around by hiding windows with orderOut: instead of destroying them.
  • firebase/firebase-ios-sdk#9371 — the same exception when a responder's class is modified at runtime. For context, gpui-base installs accessibilityHitTest: on the window's class via class_addMethod in macos_accessibility.rs; I have not shown that this is involved.

Activity

1
huacnlee1mo ago

Thanks for the thorough report. Looking at what gpui-kit does on macOS, I don't think gpui-base is involved:

  • install_window_hit_test_forwarder (crates/base/src/macos_accessibility.rs) only calls class_addMethod to add accessibilityHitTest: to the window's class. It never changes an object's isa (object_setClass), and it doesn't touch any NSView, nextResponder, or the view hierarchy. The firebase-ios-sdk#9371 failure comes from swapping the class of the observed object, which breaks its KVO registration. That isn't happening here.
  • The GPUI macOS backend (gpui-pre-macos) doesn't touch the responder chain or the Touch Bar either. The only place it adds or removes views at runtime is the BlurredView when a window's background appearance switches to or from Blurred.

Together with navop#268 hitting the same exception in another GPUI app, this looks like a timing issue between AppKit's Touch Bar finder and GPUI windows or views being created, destroyed, or activated. It's in the GPUI platform layer (or AppKit itself), and there's nothing gpui-kit can change for it. If you can narrow it down, the most useful details would be:

  1. whether a window was being opened or closed around the crash (navop's case was window close);
  2. whether any window uses WindowBackgroundAppearance::Blurred or switches background appearance at runtime;
  3. the full .ips crash report from ~/Library/Logs/DiagnosticReports, including the other threads.

If it turns out to be GPUI's macOS window/view lifecycle, it should be reported to zed-industries/zed, since the gpui-pre crates are unmodified upstream snapshots.