Crash in IndexSet.map during menu item validation in client report downloaded by Xcode

For many years I've had the following code to access the active objects of a table view in my App Store app:

class MyViewController: NSViewController: NSMenuItemValidation {

    private var tableView: NSTableView!
    private var objects = [MyObject]()

    func numberOfRows(in tableView: NSTableView) -> Int {
        return objects.count
    }
    
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        // make view for row
    }

    private var activeObjects: [MyObject] {
        return tableView?.activeRowIndexes.map({ objects[$0] }) ?? []
    }

    func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
        let activeObjects = self.activeObjects
        ...
    }

}

extension NSTableView {
    
    var activeRowIndexes: IndexSet {
        return clickedRow == -1 || selectedRowIndexes.contains(clickedRow) ? selectedRowIndexes : IndexSet(integer: clickedRow)
    }
    
}

In one of the recent updates, I wanted to add some kind of header to the table view, so I decided to add a row at the beginning and offset the indexes by 1.

    func numberOfRows(in tableView: NSTableView) -> Int {
        return objects.count + 1
    }
    
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        if row == 0 {
            // make header view
        } else {
            // make view for row - 1
        }
    }

    private var activeObjects: [MyObject] {
        return tableView?.activeRowIndexes.subtracting(IndexSet(integer: 0)).map({ objects[$0 - 1] }) ?? []
    }

But since I added this change, Xcode regularly downloads crash reports from clients crashing during menu item validation in IndexSet.map with reason Code 5 Trace/BPT trap: 5. I assumed that I was accessing an invalid array index, so I added some debug code: the crash report would then show the invalid index beside the crashed thread's name.

    private var activeObjects: [MyObject] {
        return tableView?.activeRowIndexes.subtracting(IndexSet(integer: 0)).map({ i in
            if !objects.indices.contains(i - 1) {
                Thread.current.name = (Thread.current.name ?? "") + ". Invalid index \(i - 1) for count \(objects.count)"
                preconditionFailure()
            }
            return objects[i - 1]
        }) ?? []
    }

But the crash reports for this new app version look just like the old ones and the thread name is not changed. Indeed, when recreating an invalid index access on my Mac, the crash report mentions Array._checkSubscript(_:wasNativeTypeChecked:), which does not appear in the crash reports downloaded by Xcode.

Manually symbolicating the crash report also doesn't give any more information: all lines referring to my app code are resolved to either /<compiler-generated>:0 or MyViewController.swift:0.

Apparently the problem is not an invalid array index, but something else. Does anybody have a clue what the problem could be?

(Note: the crash report mentions Sequence.compactMap because now I'm effectively calling tableView?.activeRowIndexes.compactMap, but the same crash happened before when calling IndexSet.map, which would appear in the crash report as Collection.map.)

From the stack trace, the crash most likely cause is this: objects[$0 - 1]. If $0 is out of bounds, the array access will trap.

That's what I thought too, but like I mentioned at the end, wouldn't Array._checkSubscript(_:wasNativeTypeChecked:) appear in the stack trace?

Do you agree, and have another suggestion for what the issue could be?

Crash in IndexSet.map during menu item validation in client report downloaded by Xcode
 
 
Q