Skip to content

Crash on macOS when the subclassing adapter is dropped while the view has key-value observers #823

Closed

Description

@RXNova

What happens

An app that uses the macOS SubclassingAdapter is terminated by an uncaught exception every time its window is closed. In our case this is an eframe/egui 0.36 app (winit 0.30, accesskit_winit 0.32, accesskit_macos 0.26.3) on macOS 27.0.1, on a MacBook Pro with a Touch Bar (MacBookPro17,1):

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

-[NSObject(NSKeyValueObserverRegistration) _removeObserver:forProperty:]
-[NSObject(NSKeyValueObserverRegistration) removeObserver:forKeyPath:]
-[NSObject(NSKeyValueObserverRegistration) removeObserver:forKeyPath:context:]
-[_NSTouchBarFinderObservation invalidate]
___NSTouchBarFinderSetNeedsUpdateOnMain_block_invoke_2
NSDisplayCycleObserverInvoke
NSDisplayCycleFlush

I haven't checked other macOS versions or Macs without a Touch Bar.

Cause

SubclassingAdapter::drop calls object_setClass(view, prev_class) unconditionally. AppKit registers key-value observers on the window's first responder (_NSTouchBarFinderObservation, key paths nextResponder and touchBar), so by the time the adapter is dropped, the view's real class is NSKVONotifying_AccessKitSubclassOfWinitView. Resetting the class discards KVO's subclass while the observers are still registered, and AppKit's later removeObserver:forKeyPath: raises.

Tracing addObserver:/removeObserver: in a debugger showed the observers being added while the view's class was AccessKitSubclassOfWinitView, and removed after it had been reset to WinitView.

adapters/macos/src/subclass.rs on main has the same code as 0.26.3.

What should happen

Dropping the adapter shouldn't break observers that other code has registered on the view.

How to reproduce

This program does what AppKit does, with an observer of its own. With accesskit_macos 0.27.1 it aborts at the last line (fatal runtime error: Rust cannot catch foreign exceptions, aborting).

[dependencies]
accesskit = "0.25"
accesskit_macos = "0.27.1"
objc2 = "0.5.1"
objc2-app-kit = { version = "0.2.0", features = ["NSResponder", "NSView"] }
objc2-foundation = { version = "0.2.0", features = ["NSString"] }
use std::ffi::c_void;

use accesskit::{ActionHandler, ActionRequest, ActivationHandler, TreeUpdate};
use accesskit_macos::SubclassingAdapter;
use objc2::rc::Id;
use objc2::{class, msg_send, msg_send_id};
use objc2_app_kit::NSView;
use objc2_foundation::{NSObject, NSString};

struct NoTree;

impl ActivationHandler for NoTree {
    fn request_initial_tree(&mut self) -> Option<TreeUpdate> {
        None
    }
}

struct NoActions;

impl ActionHandler for NoActions {
    fn do_action(&mut self, _request: ActionRequest) {}
}

fn main() {
    let view: Id<NSView> = unsafe { msg_send_id![class!(NSView), new] };
    let adapter = unsafe {
        SubclassingAdapter::new(&*view as *const NSView as *mut c_void, NoTree, NoActions)
    };

    // Stand-in for AppKit's own observers on the first responder.
    let observer = NSObject::new();
    let key_path = NSString::from_str("nextResponder");
    let context: *mut c_void = std::ptr::null_mut();
    let _: () = unsafe {
        msg_send![&view, addObserver: &*observer, forKeyPath: &*key_path, options: 0usize, context: context]
    };

    drop(adapter);

    // Raises NSRangeException.
    let _: () = unsafe { msg_send![&view, removeObserver: &*observer, forKeyPath: &*key_path] };
}

Activity

  1. lumirth commented on Oct 16, 2026

    @lumirth

    Also experiencing this. Would love to see it fixed.

  2. RXNova commented on Oct 17, 2026

    @RXNova
    Author

    Hey @DataTriny can you please help in review or adapting this fix. Thanks.

  3. DataTriny commented on Oct 18, 2026

    @DataTriny
    Member

    I don't have a Mac with a touchbar. Help testing #835 would be appreciated. Thanks.

  4. DataTriny commented on Oct 19, 2026

    @DataTriny
    Member

    Searching GitHub I have found a few instances of people reporting that we could sometimes get an already observed view which would also crash. If this turns out to be true, even if the subclassing adapter is installed before the view is shown for the first time, then the entire subclassing technique would be flawed.

    @RXNova, @lumirth, if you have a Mac with a touchbar, your help settling this would be useful.

    Thanks.

  5. jacquetc commented on Oct 21, 2026

    @jacquetc

    Hello, I applied your patch for a user of Skribisto (which uses Teksilo framework, which is heavily using AccessKit). The user have a Touch bar on a mac. It works after your patch.

    Reddit with the issue
    Skribisto test release

    If you need more information, I'll be happy to help.

  6. DataTriny commented on Oct 21, 2026

    @DataTriny
    Member

    Thanks @jacquetc,

    I'll do my best to release a patched version of accesskit_macos and accesskit_ios before the end of this week. Let's hope that applications crashing at startup are either not a thing or misusing the library.

  7. DataTriny commented on Oct 23, 2026

    @DataTriny
    Member

    Fixed in #835

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions