#24909·qBittorrent

macOS: accessibilitySelectedChildren crash followed by crash-dialog hang

Author: NathanDrake2406Created Sep 13, 2026Updated Sep 14, 2026
LabelsOS: macOSCrash

qBittorrent & operating system versions

  • qBittorrent: 5.2.3, arm64
  • macOS: 26.6.2 (25G83), Apple Silicon
  • Qt: 6.10.3 (version string in the installed executable)
  • AltTab: 11.6.1 (installed app version)
  • libtorrent: 1.2.20

What is the problem?

qBittorrent becomes unresponsive after clicking a control shortly after launch. This also occurs after quitting AltTab, so the initial attribution to AltTab was incorrect. Expected behaviour: accessibility queries should not crash or hang qBittorrent.

The attached stack excerpt comes from a macOS hang report on 2026-09-13. The process was approximately 30 seconds old. The hang lasted 25.50 seconds; sampling covered the final 4.10 seconds after 21 seconds of unresponsiveness. The main thread consumed 3.996 seconds of CPU during those 4.10 seconds.

The stack shows QMacAccessibilityElement::accessibilitySelectedChildren + 244, followed by a signal trampoline, abnormalExitHandler(int), and QDialog::exec(). Inside that nested event loop another accessibility request enters accessibilitySelectedChildren again. This suggests an initial fatal signal in the accessibility selection path followed by a secondary hang while the crash dialog processes accessibility requests. The hang report does not identify the signal or the exact invalid object.

Steps to reproduce

Run qBittorrent 5.2.3 on the environment above, then click a control in qBittorrent. The failure was observed with AltTab running and again after quitting AltTab. The exact clicked control and selection state still need to be established; no specific file-priority change is claimed for this report.

Additional context

Potentially related: #24901, which addresses a crash in the same Qt function during Contents priority changes. It is not yet established whether that patch covers this reproduction.

A second report captured at 21:37 on 2026-09-13, after AltTab was quit, shows the same accessibilitySelectedChildren → fatal-signal handler → nested crash-dialog accessibility request. It is from the same original qBittorrent executable (UUID 3EB4BD78-471B-3479-A7B6-7661A7205AD1). AltTab is not required. The reports do not establish which accessibility client initiates the failing request.

Follow-up: reproduced Qt interface ownership failures

A local build with the initial candidate patch still crashed when removing a torrent. The initial guards only protected synthetic elements borrowing the table ID; that fix was incomplete.

The new report contains QAccessible::updateAccessibility and QTreeView::rowsRemoved after TransferListModel::handleTorrentAboutToBeRemoved. A native Qt 6.10.3 reproduction using the first patched plugin produces the same null dereference at QtWidgets offset 0x29a1ac in the table's model-change handling.

Resolved cells also remain owned by the table interface's child-ID cache. Destroying native rows deleted those cell interfaces, leaving stale IDs in that cache. The revised patch removes interface deletion from Cocoa element invalidation and teardown entirely. Interface lifetime stays with Qt's accessibility cache and view; normal cache deletion still invalidates native elements.

Verification with a rebuilt Qt 6.10.3 Cocoa plugin:

  • First patch: the row-cleanup/removal reproduction crashes at the same instruction as the new report. A strengthened lifetime assertion also fails against that build.
  • Revised patch: 600 selection/collapse/sort cycles and 100 native-row cleanup/removal/insertion cycles pass. Checks cover live cell interfaces, row counts, selection, and cached cell/table cleanup when the view is destroyed.
  • The focused treeViewSyntheticElementsPreserveTable C++ test passes, including unresolved and resolved cells and row removal.
  • qBittorrent 5.2.3 local build 52302 starts with a separate empty profile and loads its bundled patched plugin. The original user-data workflow still needs confirmation. The full Qt suite was not run.

Revised production change:

diff
diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h
index a7fc0bd..eb8f841 100644
--- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h
+++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h
@@ -19,7 +19,6 @@ QT_DECLARE_NAMESPACED_OBJC_INTERFACE(QMacAccessibilityElement, NSObject <NSAcces
 - (instancetype)initWithId:(QAccessible::Id)anId role:(NSAccessibilityRole)role;
 + (instancetype)elementWithId:(QAccessible::Id)anId;
 + (instancetype)elementWithInterface:(QAccessibleInterface *)iface;
-+ (void)removeElementsFromCache:(NSArray *)array;
 - (void)updateTableModel;
 - (QAccessibleInterface *)qtInterface;
 )
diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm
index 18b8715..c4dd136 100644
--- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm
+++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm
@@ -216,25 +216,14 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
     return [self elementWithId:anId];
 }
 
-+ (void)removeElementsFromCache:(NSArray *)array {
-    for (uint i = 0; i < array.count; ++i) {
-        QMacAccessibilityElement *cell = [array objectAtIndex:i];
-        if (cell->axid) { // it's a proper cell, remove from cache
-            QAccessibleCache::instance()->deleteInterface(cell->axid);
-        }
-    }
-}
-
 // called by QAccessibleCache::removeAccessibleElement, which also releases
 - (void)invalidate {
     axid = 0;
     if (rows) {
-        [QMacAccessibilityElement removeElementsFromCache:rows];
         [rows autorelease];
         rows = nil;
     }
     if (columns) {
-        [QMacAccessibilityElement removeElementsFromCache:columns];
         [columns autorelease];
         columns = nil;
     }
@@ -243,27 +232,16 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
     NSAccessibilityPostNotification(self, NSAccessibilityUIElementDestroyedNotification);
 }
 
-/*!
-    \internal
-
-    If this element represents a table, then the rows and columns array are both
-    populated with elements representing the rows and columns. If this elements
-    represents a row, then the columns array is populated with elements
-    representing the cells. Not all of those synthesized elements might be in
-    the cache, but those that are need to be removed so that we don't end up
-    with stale representations of children when the higher-level element
-    expires.
-*/
+// The accessibility cache and the view own the QAccessibleInterfaces. Releasing
+// a native row or cell must not delete those interfaces: the table still caches
+// their IDs and uses them when processing subsequent model changes.
 - (void)dealloc {
     if (rows) {
-        [QMacAccessibilityElement removeElementsFromCache:rows];
         [rows release]; // will also release all entries first
     }
     if (columns) {
-        [QMacAccessibilityElement removeElementsFromCache:columns];
         [columns release]; // will also release all entries first
     }
-    QAccessibleCache::instance()->deleteInterface(axid);
     [super dealloc];
 }
 

The source patch and regression are prepared against Qt dev, with the actual plugin and focused test built against version-matched Qt 6.10.3. Qt requires Gerrit submission rather than GitHub pull requests; no upstream Gerrit review has been submitted yet.

Log(s) & preferences file(s)

Relevant main-thread stack below. The unrelated system-wide process dump and user preferences are omitted.

Heaviest stack for the main thread of the target process:
  41  start + 6992 (dyld + 132324) [0x1875044e4]
  41  main + 1616 (qbittorrent + 154144) [0x100df9a20]
  41  Application::exec() + 540 (qbittorrent + 69484) [0x100de4f6c]
  41  QCoreApplication::exec() + 276 (qbittorrent + 16722164) [0x101dc68f4]
  41  QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) + 608 (qbittorrent + 16738264) [0x101dca7d8]
  41  QCocoaEventDispatcher::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 1156 (qbittorrent + 4263112) [0x1011e4cc8]
  41  -[NSApplication run] + 368 (AppKit + 180540) [0x18bdb513c]
  41  -[NSApplication(NSEventRouting) nextEventMatchingMask:untilDate:inMode:dequeue:] + 72 (AppKit + 12379768) [0x18c957678]
  41  -[NSApplication(NSEventRouting) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 688 (AppKit + 12380524) [0x18c95796c]
  41  _DPSNextEvent + 576 (AppKit + 233604) [0x18bdc2084]
  41  _DPSBlockUntilNextEventMatchingListInMode + 228 (AppKit + 7230416) [0x18c46e3d0]
  41  _BlockUntilNextEventMatchingListInMode + 48 (HIToolbox + 2400588) [0x19490414c]
  41  ReceiveNextEventCommon + 488 (HIToolbox + 788668) [0x19477a8bc]
  41  RunCurrentEventLoopInMode + 320 (HIToolbox + 775520) [0x194777560]
  41  _CFRunLoopRunSpecificWithOptions + 532 (CoreFoundation + 1372772) [0x187a5e264]
  41  __CFRunLoopRun + 2168 (CoreFoundation + 511432) [0x18798bdc8]
  41  __CFRunLoopDoSource1 + 504 (CoreFoundation + 517120) [0x18798d400]
  41  __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 60 (CoreFoundation + 517336) [0x18798d4d8]
  41  mshMIGPerform + 208 (HIServices + 19924) [0x18ef7add4]
  41  _XCopyHierarchy + 588 (HIServices + 302868) [0x18efbff14]
  41  _AXXMIGCopyHierarchy + 1588 (HIServices + 162200) [0x18ef9d998]
  41  __AXCopyAttributeValueForHierarchy + 156 (HIServices + 169180) [0x18ef9f4dc]
  41  GetAttributeValueCount + 180 (AppKit + 8243660) [0x18c5659cc]
  41  -[NSObject(NSAccessibilityInternal) _accessibilityArrayAttributeCount:clientError:] + 120 (AppKit + 8229368) [0x18c5621f8]
  41  -[NSObject(NSObjectAccessibilityAttributeAccessAdditions) accessibilityArrayAttributeCount:] + 76 (AppKit + 8225164) [0x18c56118c]
  41  _NSAccessibilityEntryPointValueForAttribute + 224 (AppKit + 2611796) [0x18c006a54]
  41  NSAccessibilityPerformEntryPointObject + 44 (AppKit + 10784544) [0x18c7d1f20]
  41  ___NSAccessibilityEntryPointValueForAttribute_block_invoke.773 + 1520 (AppKit + 10802292) [0x18c7d6474]
  41  -[NSAccessibilityAttributeAccessorInfo getAttributeValue:forObject:] + 60 (AppKit + 745048) [0x18be3ee58]
  41  -[QMacAccessibilityElement accessibilitySelectedChildren] + 244 (qbittorrent + 4557288) [0x10122c9e8]
  41  _sigtramp + 56 (libsystem_platform.dylib + 14148) [0x1878d9744]
  41  (anonymous namespace)::abnormalExitHandler(int) + 460 (qbittorrent + 162548) [0x100dfbaf4]
  41  QDialog::exec() + 224 (qbittorrent + 11291540) [0x101898b94]
  41  QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) + 608 (qbittorrent + 16738264) [0x101dca7d8]
  41  QCocoaEventDispatcher::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 896 (qbittorrent + 4262852) [0x1011e4bc4]
  41  qt_mac_waitForMoreEvents(NSString*) + 80 (qbittorrent + 4264684) [0x1011e52ec]
  41  -[NSApplication(NSEventRouting) nextEventMatchingMask:untilDate:inMode:dequeue:] + 72 (AppKit + 12379768) [0x18c957678]
  41  -[NSApplication(NSEventRouting) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 688 (AppKit + 12380524) [0x18c95796c]
  41  _DPSNextEvent + 576 (AppKit + 233604) [0x18bdc2084]
  41  _DPSBlockUntilNextEventMatchingListInMode + 228 (AppKit + 7230416) [0x18c46e3d0]
  41  _BlockUntilNextEventMatchingListInMode + 48 (HIToolbox + 2400588) [0x19490414c]
  41  ReceiveNextEventCommon + 488 (HIToolbox + 788668) [0x19477a8bc]
  41  RunCurrentEventLoopInMode + 320 (HIToolbox + 775520) [0x194777560]
  41  _CFRunLoopRunSpecificWithOptions + 532 (CoreFoundation + 1372772) [0x187a5e264]
  41  __CFRunLoopRun + 2168 (CoreFoundation + 511432) [0x18798bdc8]
  41  __CFRunLoopDoSource1 + 504 (CoreFoundation + 517120) [0x18798d400]
  41  __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 60 (CoreFoundation + 517336) [0x18798d4d8]
  41  mshMIGPerform + 208 (HIServices + 19924) [0x18ef7add4]
  41  _XCopyMultipleAttributeValues + 448 (HIServices + 301880) [0x18efbfb38]
  41  _AXXMIGCopyMultipleAttributeValues + 436 (HIServices + 151660) [0x18ef9b06c]
  41  CopyMultipleAttributeValues + 880 (AppKit + 8246624) [0x18c566560]
  41  CopyAppKitUIElementAttributeValueNoCatch + 92 (AppKit + 8252120) [0x18c567ad8]
  41  -[NSObject(NSAccessibilityInternal) _accessibilityValueForAttribute:clientError:] + 272 (AppKit + 8228216) [0x18c561d78]
  41  _NSAccessibilityEntryPointValueForAttribute + 224 (AppKit + 2611796) [0x18c006a54]
  41  NSAccessibilityPerformEntryPointObject + 44 (AppKit + 10784544) [0x18c7d1f20]
  41  ___NSAccessibilityEntryPointValueForAttribute_block_invoke.773 + 1520 (AppKit + 10802292) [0x18c7d6474]
  41  -[NSAccessibilityAttributeAccessorInfo getAttributeValue:forObject:] + 60 (AppKit + 745048) [0x18be3ee58]
  36  -[QMacAccessibilityElement accessibilitySelectedChildren] + 188 (qbittorrent + 4557232) [0x10122c9b0]
 *36  ??? (kernel.release.t8132 + 24220) [0xfffffe000b73de9c]
 *36  ??? (kernel.release.t8132 + 1914436) [0xfffffe000b90b644]
 *35  ??? (kernel.release.t8132 + 1918824) [0xfffffe000b90c768]
 *32  ??? (kernel.release.t8132 + 362284) [0xfffffe000b79072c]
 *29  ??? (kernel.release.t8132 + 360420) [0xfffffe000b78ffe4]
 *29  ??? (kernel.release.t8132 + 952052) [0xfffffe000b8206f4]
 *16  ??? (kernel.release.t8132 + 386680) [0xfffffe000b796678]
 *8   ??? (kernel.release.t8132 + 200716) [0xfffffe000b76900c]
 *7   ??? (kernel.release.t8132 + 188524) [0xfffffe000b76606c]
 *7   ??? (kernel.release.t8132 + 188524) [0xfffffe000b76606c]