ASMigrationDisplayItem migration fails after app reinstall, ASErrorDomain 500 "Picker already activated" until phone reboot

Environment

  • iOS: 26.6
  • Xcode: 26.3

I’m encountering an issue migrating legacy Bluetooth peripherals using ASMigrationDisplayItem from AccessorySetupKit. I have verified that both bluetoothServiceUUID and peripheralIdentifier are correct.

  1. On the first‑time use of ASMigrationDisplayItem, the peripheral migrates successfully and shows up in iOS Settings.
  2. After deleting the app, the accessory authorization list is cleared. Upon reinstalling the app, migration attempts do nothing — no device appears in Settings. Occasionally I get this error:

Error Domain=ASErrorDomain Code=500 "(null)" Picker already activated

  1. All subsequent migration attempts keep failing. The migration only works again after a full phone reboot.

My hypothesis: When AccessorySetupKit is first used, a system daemon is spawned to handle AccessorySetupKit‑related tasks. After app deletion, this daemon is not properly terminated. Reinstalling the app later causes interference that breaks ASMigrationDisplayItem.

This issue has blocked my development for a long time. I found a similar unresolved thread on Apple Developer Forums: https://developer.apple.com/forums/thread/839512

Questions:

  1. Is there any workaround I can adopt right now?
  2. Could Apple please investigate and fix this bug?

After deleting the app, the accessory authorization list is cleared. Upon reinstalling the app, migration attempts do nothing — no device appears in Settings. Occasionally I get this error:

I'm not sure what your expectation here is. Why would you be able to migrate an accessory at all after your app had been deleted?

Similarly:

All subsequent migration attempts keep failing. The migration only works again after a full phone reboot.

I'm not sure why it's working at all after the reboot, but why would an end user be doing this?

Could Apple please investigate and fix this bug?

Have you filed a bug and, if so, what's the bug number?

Is there any workaround I can adopt right now?

For development purposes, what's the issue with rebooting the the device?

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

I’ve run several tests to verify this issue before filing a bug report. I would appreciate your advice.

Key facts about my Bluetooth accessory:

  • It is not paired with system Bluetooth.
  • I only persist its peripheralIdentifier and bluetoothServiceUUID for later accessory migration.

Reproduction steps (intermittent)

  1. Launch the app while applicationState is .active. Call showPicker with ASMigrationDisplayItem, migration succeeds.
  2. Delete and reinstall the app, then repeat step 1. It fails probabilistically. Once it fails, all subsequent showPicker calls keep failing.
  3. Rebooting the iPhone restores normal behavior.
  4. Failure can also occur on the very first step‑1 attempt.

Additional observations from testing

  1. When using ASMigrationDisplayItem, the picker UI never appears, whether the call succeeds or fails.
  2. Intermittent error observed: XPC connection invalid.
  3. After the first failure, both ASPickerDisplayItem and ASMigrationDisplayItem showPicker calls consistently fail with:
Picker already activated
Error Domain=ASErrorDomain Code=500 "(null)"
  1. Only an iPhone reboot can recover from this broken state.

Sample demo code

import UIKit
import CoreBluetooth
import AccessorySetupKit
public class BluetoothService {
    
    public static let share = BluetoothService()
    
    private var session: Any?
    
    private let image: UIImage = UIImage(systemName: "lanyardcard") ?? UIImage()
    
    public func activeSession() {
        if #available(iOS 18.4, *) {
            let session = ASAccessorySession()
            self.session = session
            
            session.activate(on: .main) { [weak self] event in
                guard let self = self else { return }
                self.handleASKSessionEvent(event)
            }
        }
    }
    
    @available(iOS 18.4, *)
    private func handleASKSessionEvent(_ event: ASAccessoryEvent) {
        switch event.eventType {
        case .activated:
            self.migration()
            break
        default:
            break
        }
    }
    @available(iOS 18.4, *)
    public func migration() {
        guard UIApplication.shared.applicationState == .active else {
            return
        }
        guard let session = self.session as? ASAccessorySession else { return }
        
        let name = "My-DeVice-A"
        let identifier = "C4B8F5B1-B37B-2B08-AAC5-BF48EF2DCB44"
        let descriptor = ASDiscoveryDescriptor()
        descriptor.bluetoothServiceUUID = CBUUID(string: "1234")
        descriptor.bluetoothNameSubstring = name
        let migration = ASMigrationDisplayItem(
            name: name,
            productImage: image,
            descriptor: descriptor
        )
        migration.peripheralIdentifier = UUID(uuidString: identifier)
        
        session.showPicker(for: [migration]) { error in
            if let error = error {
                print("showPicker error: \(error)")
            } else {
                print("showPicker success")
            }
        }
        
    }
    
    @available(iOS 18.4, *)
    public func showPicker() {
        guard UIApplication.shared.applicationState == .active else {
            return
        }
        
        guard let session = self.session as? ASAccessorySession else { return }
        let name = "My-DeVice-B"
        let descriptor = ASDiscoveryDescriptor()
        descriptor.bluetoothServiceUUID = CBUUID(string: "1234")
        descriptor.bluetoothNameSubstring = name
        
        let displayItem = ASPickerDisplayItem(
            name: name,
            productImage: image,
            descriptor: descriptor
        )
        
        session.showPicker(for: [displayItem]) { error in
            if let error = error {
                print("showPicker error: \(error)")
            } else {
                print("showPicker success")
            }
        }
        
    }
    
}

I’m looking forward to any suggestions to resolve this issue.

ASMigrationDisplayItem migration fails after app reinstall, ASErrorDomain 500 "Picker already activated" until phone reboot
 
 
Q