CarPlay handoff to MapKit fails on n+1 attempts

A CarPlay app with the carplay-fueling entitlement displays a list of stations. Tapping a station hands off to the Maps app for directions and navigation.

Sometimes the handoff will return unsuccessful from the open call. The first attempt will succeeed. I immedediately return to my app and select a different station. The second attemp may succeed or it may fail.

If the handoff attempt fails I can switch to the Maps app, return to MyApp, tap the same station row that just failed and the handoff will succeed.

The issue is across multiple iOS versions (18 and 26) and multiple devices.

Looking at the sysdiagnose logs, a successful handoff looks like this:

2026-06-26 15:56:27.762040
lsd: pid 14570 requests to open URL with scheme <private>

2026-06-26 15:56:27.768475
lsd: [FBSSystemService][0xee43] Sending request to open "com.apple.Maps"

2026-06-26 15:56:27.782145
lsd: [FBSSystemService][0xee43] Request successful:
<BSProcessHandle ... Maps:17170; valid: YES>

A failed handoff looks like this:

2026-06-26 17:05:00.162432
lsd: pid 14570 requests to open URL with scheme <private>

2026-06-26 17:05:00.171618
lsd: [FBSSystemService][0x3d16] Sending request to open "com.apple.Maps"

2026-06-26 17:05:00.173776
SpringBoard: Received request to open "com.apple.Maps" with url "maps:<private>"
from lsd:122 on behalf of MyApp:14570.

2026-06-26 17:05:00.174110
SpringBoard: Received untrusted open application request for "com.apple.Maps"
from <FBApplicationProcess ... app<au.com.philk.MyApp>:14570>

2026-06-26 17:05:00.175103
SpringBoard: Open "com.apple.Maps" request from lsd:122 failed with error:
FBSOpenApplicationServiceErrorDomain; code: 1 ("RequestDenied")


Reason:
Application au.com.philk.MyApp is neither visible nor entitled,
so may not perform un-trusted user actions.

Underlying:
FBSOpenApplicationErrorDomain; code: 3 ("Security")

Looking more closely at the logs, the template UI is brought forward:

CarPlay: DBApplicationSceneHostViewController; au.com.philk.MyApp; proxy: com.apple.CarPlayTemplateUIHost

RunningBoard briefly grants MyApp foreground / render assertions:

Foreground Template App FBWorkspace (ForegroundFocal) Set darwin role to: UserInteractiveFocal visibility is yes

However, SpringBoard also records MyApp as background:

2026-06-26 17:04:53.709527 SpringBoard: Application process state changed for au.com.philk.MyApp: taskState: Running; visibility: Background

The denied open at 17:05:00 appears to use SpringBoard's application visibility/trust decision, not the fact that the user is actively interacting with MyApp's CarPlay template UI.

The following is logged:

Application au.com.philk.MyApp is neither visible nor entitled, so may not perform un-trusted user actions.

I have tried two different ways to pass/open the URL, and both will fail, usually after the first attempt.

  • A plain maps:// URL
  • Creating a MapItem and using:

MKMapItem.openInMaps(launchOptions:from:completionHandler:)

I wonder if this is the same or similar bug to the one reported here? https://developer.apple.com/forums/thread/787788?answerId=843556022#843556022

What is the root cause of the random failures? Any help or pointers will be appreciated.

As recommended by DTS, below is a more detailed description of the issue. It includes links to a sample project and a sysdiagnose generated by the project.

Intermittent CarPlay Handoff to Maps Failure

Summary

Nick out for fuel (DadCost1a) is a CarPlay fueling app using the CarPlay fueling entitlement:

It was previously recommended that apps using the fueling entitlement hand a driving destination coordinate off to Apple Maps. DadCost1a follows that model: when the user selects a station in the CarPlay UI, the app asks Apple Maps to open driving directions to that station.

The handoff is intermittent within the same CarPlay session. The same app process, same CPListItem selection path, same maps: URL path, and same Maps process can succeed and then fail seconds later. A normal use of the app is for a person to make a selection, see the route, then return to DadCost1a to make a second selection.

A typical sysdiagnose report of the failure can be seen here:
<https://museassisted.com.au/DTS/sysdiagnose.tar.gz> (308 MB)

A sample project that can repeat the failures is here:
<https://museassisted.com.au/DTS/CarPlay_SampleProject.zip> (112 KB)

The four different handoff methods in the sample code all show the same “is neither visible nor entitled” error, moreso when the vehicle is in motion.

The important pattern in the sysdiagnose captures is:

  • Successful DadCost1a -&gt; Maps opens are received by the CarPlay process and succeed.
  • Failed DadCost1a -&gt; Maps opens are received by SpringBoard and denied.
  • The denied requests use the same DadCost1a process and are rejected with:
Application au.com.philk.DadCost1aus is neither visible nor entitled,
so may not perform un-trusted user actions.

The same failure is seen across multiple devices and iOS 17, 18, and 26.

I am hoping DTS can confirm the supported Maps handoff API for a CarPlay fueling app, and whether this is expected behavior when the app's visible CarPlay UI is mediated through CarPlayTemplateUIHost.

Current Implementation

The CarPlay scene delegate keeps the connected CPTemplateApplicationScene and CPInterfaceController from the CarPlay scene connection:

func templateApplicationScene(
    _ templateApplicationScene: CPTemplateApplicationScene,
    didConnect interfaceController: CPInterfaceController
) {
    self.templateApplicationScene = templateApplicationScene
    self.interfaceController = interfaceController
    sessionConfiguration = CPSessionConfiguration(delegate: self)
    ...
    refreshRootTemplate()
}

The station tab builds CarPlay list sections from the current trip result. Selecting a station calls openDirectionsInCarPlayNavigationApp(to:):

case let .loaded(tripResult):
    sections = CarPlayStationsFormatter.sections(for: tripResult) { [weak self] station in
        self?.openDirectionsInCarPlayNavigationApp(to: station)
    }

The CPListItem handler invokes the station selection callback and then immediately calls the CarPlay list-item completion handler:

let stationItems: [CPListItem] = sortedStations.prefix(12).map { station in
    let item = CPListItem(
        text: stationPrimaryText(for: station),
        detailText: stationDetailText(for: station)
    )
    item.setImage(routeSideLeadingImage(for: station, showBothSides: tripResult.request.showBothSides))
    item.isEnabled = true
    item.handler = { _, completion in
        onSelectStation?(station)
        completion()
    }
    return item
}

The current Maps handoff code uses the connected CPTemplateApplicationScene, constructs a maps: URL, and calls scene.open(url, options: nil):

func openDirectionsInCarPlayNavigationApp(to station: OneWayTripStationResult) {
    guard let scene = templateApplicationScene,
          let url = simpleMapsURL(for: station) else {
        presentMapsOpenFailureAlert(for: station)
        return
    }

    if shouldClearStationSelectionAfterMapsReturn {
        isAwaitingMapsReturn = true
    }

    scene.open(url, options: nil) { [weak self] success in
        guard !success else {
            return
        }

        Task { @MainActor in
            self?.isAwaitingMapsReturn = false
            self?.presentMapsOpenFailureAlert(for: station)
        }
    }
}

The URL currently contains only the destination coordinate and driving directions flag:

private func simpleMapsURL(for station: OneWayTripStationResult) -> URL? {
    let coordinate = station.coordinate
    guard CLLocationCoordinate2DIsValid(coordinate) else {
        return nil
    }

    var components = URLComponents()
    components.scheme = "maps"
    components.host = ""
    components.queryItems = [
        URLQueryItem(
            name: "daddr",
            value: String(
                format: "%.8f,%.8f",
                locale: Locale(identifier: "en_US_POSIX"),
                coordinate.latitude,
                coordinate.longitude
            )
        ),
        URLQueryItem(name: "dirflg", value: "d")
    ]
    return components.url
}

If the open fails, DadCost1a presents a CarPlay alert:

private func presentMapsOpenFailureAlert(for station: OneWayTripStationResult) {
    presentMapsAlert(titleVariants: ["Unable to open Maps"])
}

The project entitlement file includes:

<key>com.apple.developer.carplay-fueling</key>
<true/>

Opening Methods and Timing Changes Already Tried

I have tried several Maps-opening approaches while diagnosing this issue. The active implementation is currently the raw maps: URL with CPTemplateApplicationScene.open(...), but the project history includes the attempts below.

1. Raw maps: URL with search query and coordinate

This version used CPTemplateApplicationScene.open(...) with a maps: URL containing the station name and coordinate:

var components = URLComponents()
components.scheme = "maps"
components.host = ""
components.queryItems = [
    URLQueryItem(name: "q", value: station.stationName),
    URLQueryItem(
        name: "ll",
        value: String(
            format: "%.8f,%.8f",
            locale: Locale(identifier: "en_US_POSIX"),
            coordinate.latitude,
            coordinate.longitude
        )
    )
]

scene.open(url, options: nil) { success in
    ...
}

This could open Maps, but the CarPlay handoff remained intermittent.

2. Raw maps: URL with destination and driving flag

The current active version uses daddr and dirflg=d:

var components = URLComponents()
components.scheme = "maps"
components.host = ""
components.queryItems = [
    URLQueryItem(
        name: "daddr",
        value: String(
            format: "%.8f,%.8f",
            locale: Locale(identifier: "en_US_POSIX"),
            coordinate.latitude,
            coordinate.longitude
        )
    ),
    URLQueryItem(name: "dirflg", value: "d")
]

scene.open(url, options: nil) { success in
    ...
}

I also briefly tried adding the Maps type parameter:

URLQueryItem(name: "t", value: "m")

The July 2 sysdiagnose in this report was captured with the raw maps: URL approach active. It shows that the URL is resolved to com.apple.Maps; the failure is the SpringBoard/FrontBoard trust denial, not URL parsing. Adding the t=m item to the URL has increased the number of failures seen.

3. Scene-aware MapKit single-item API

I tried replacing the URL with MKMapItem.openInMaps(launchOptions:from:completionHandler:), using the connected CPTemplateApplicationScene as the source scene:

let placemark = MKPlacemark(coordinate: coordinate)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = station.stationName

let launchOptions: [String: Any] = [
    MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving
]

mapItem.openInMaps(launchOptions: launchOptions, from: scene) { success in
    ...
}

This is the API that seems semantically closest to a CarPlay scene-originated Maps handoff. In testing, however, this did not eliminate the failure; it appeared to fail more often than the raw URL path, so the active code was reverted to CPTemplateApplicationScene.open(...).

4. Scene-aware MapKit class API

I also tried MKMapItem.openMaps(with:launchOptions:from:completionHandler:):

let launchOptions = [
    MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving
]

MKMapItem.openMaps(with: [mapItem], launchOptions: launchOptions, from: scene) { success in
    ...
}

This was tested with state around the handoff to avoid duplicate opens:

isOpeningDirectionsInMaps = true
isAwaitingMapsReturn = true
refreshStationsTemplateAfterMapsReturn()

5. MapKit first, URL fallback second

One version used MapKit first and then fell back to a URL open if MapKit returned failure:

MKMapItem.openMaps(with: [mapItem], launchOptions: launchOptions, from: scene) { success in
    guard !success else {
        return
    }

    self.openDirectionsWithURLFallback(to: station, from: scene, attemptID: attemptID)
}

The fallback used an Apple Maps web URL:

var components = URLComponents()
components.scheme = "http"
components.host = "maps.apple.com"
components.queryItems = [
    URLQueryItem(name: "daddr", value: destination),
    URLQueryItem(name: "dirflg", value: "d")
]

scene.open(url, options: nil) { success in
    ...
}

A previous web-style variant was also considered:

components.scheme = "https"
components.host = "maps.apple.com"
components.path = "/directions"
components.queryItems = [
    URLQueryItem(name: "destination", value: destination),
    URLQueryItem(name: "mode", value: "driving")
]

That web-style variant could resolve outside the intended native Maps CarPlay path, so it was not kept.

6. CarPlay list-selection timing changes

I also tried changing the timing of the handoff relative to the CPListItem completion handler.

One version passed the list-item completion into the Maps handoff and called completion() from inside the Maps open completion:

func openDirectionsInCarPlayNavigationApp(
    to station: OneWayTripStationResult,
    completion: @escaping () -> Void
) {
    guard let scene = templateApplicationScene,
          let url = simpleMapsURL(for: station) else {
        completion()
        presentMapsOpenFailureAlert(for: station)
        return
    }

    scene.open(url, options: nil) { success in
        completion()
        ...
    }
}

Another version completed the CarPlay list selection first and delayed the Maps open slightly so the row could finish deselecting before cross-app handoff:

completion()
pendingMapsOpenWorkItem?.cancel()

let workItem = DispatchWorkItem { [weak self, weak scene] in
    guard let self, let scene else {
        return
    }

    self.pendingMapsOpenWorkItem = nil
    self.openDirectionsWhenReady(to: station, from: scene)
}

pendingMapsOpenWorkItem = workItem
DispatchQueue.main.asyncAfter(
    deadline: .now() + mapsHandoffSelectionDelay,
    execute: workItem
)

These timing changes improved some UI behavior around selected rows and repeated taps, but they did not resolve the underlying Maps handoff denial.

Successful Handoff Evidence

The same DadCost1a process (pid 7527) successfully opens Maps earlier in the same capture window. In these successful cases the open request is received by the CarPlay process, not SpringBoard.

First successful handoff:

2026-07-02 17:26:13.891469
DadCost1a[7527]: selected list item 81B1A765-63EA-4FEF-903A-7F916F42B5B5

2026-07-02 17:26:13.896489
lsd[1878]: pid 7527 requests to open URL with scheme <private>

2026-07-02 17:26:13.906381
lsd[1878]: [FBSSystemService][0x7be7] Sending request to open "com.apple.Maps"

2026-07-02 17:26:13.908538
CarPlay[689]: [FBSystemService][0x7be7] Received request to open
"com.apple.Maps" with url "maps:<private>" from lsd:1878
on behalf of DadCost1a:7527.

2026-07-02 17:26:13.919213
lsd[1878]: [FBSSystemService][0x7be7] Request successful:
<BSProcessHandle ... Maps:7790; valid: YES>

Second successful handoff:

2026-07-02 17:26:17.592621
DadCost1a[7527]: selected list item A01C9B1A-6C76-4E6A-9C33-241684F0BBB2

2026-07-02 17:26:17.594346
lsd[1878]: pid 7527 requests to open URL with scheme <private>

2026-07-02 17:26:17.603387
lsd[1878]: [FBSSystemService][0x7d5a] Sending request to open "com.apple.Maps"

2026-07-02 17:26:17.606043
CarPlay[689]: [FBSystemService][0x7d5a] Received request to open
"com.apple.Maps" with url "maps:<private>" from lsd:1878
on behalf of DadCost1a:7527.

2026-07-02 17:26:17.614681
lsd[1878]: [FBSSystemService][0x7d5a] Request successful:
<BSProcessHandle ... Maps:7790; valid: YES>

Failed Handoff Evidence

Seconds after the successful handoffs above, DadCost1a receives the same kind of CarPlay list selection, and lsd again attempts to open com.apple.Maps with a maps: URL on behalf of DadCost1a. These requests are received by SpringBoard instead of CarPlay and are denied.

First failed handoff:

2026-07-02 17:26:22.024941
DadCost1a[7527]: selected list item E2029AC8-C914-4808-9C87-3357075664EC

2026-07-02 17:26:22.027173
lsd[1878]: pid 7527 requests to open URL with scheme <private>

2026-07-02 17:26:22.036393
lsd[1878]: [FBSSystemService][0xbadb] Sending request to open "com.apple.Maps"

2026-07-02 17:26:22.038612
SpringBoard[36]: [FBSystemService][0xbadb] Received request to open
"com.apple.Maps" with url "maps:<private>" from lsd:1878
on behalf of DadCost1a:7527.

2026-07-02 17:26:22.038943
SpringBoard[36]: Received untrusted open application request for
"com.apple.Maps" from <FBApplicationProcess ... app<au.com.philk.DadCost1aus>:7527>

2026-07-02 17:26:22.039540
SpringBoard[36]: Open "com.apple.Maps" request from lsd:1878 failed with error:
FBSOpenApplicationServiceErrorDomain; code: 1 ("RequestDenied")

Reason:
Application au.com.philk.DadCost1aus is neither visible nor entitled,
so may not perform un-trusted user actions.

Underlying:
FBSOpenApplicationErrorDomain; code: 3 ("Security")

2026-07-02 17:26:22.041177
DadCost1a[7527]: Failed to open URL <private>:
Error Domain=LSApplicationWorkspaceErrorDomain Code=115

Second failed handoff, immediately before the sysdiagnose was captured:

2026-07-02 17:26:24.791393
DadCost1a[7527]: selected list item 77B1F40D-4607-49EE-8A30-DCB85F9C7ACD

2026-07-02 17:26:24.793178
lsd[1878]: pid 7527 requests to open URL with scheme <private>

2026-07-02 17:26:24.802371
lsd[1878]: [FBSSystemService][0x94c8] Sending request to open "com.apple.Maps"

2026-07-02 17:26:24.804862
SpringBoard[36]: [FBSystemService][0x94c8] Received request to open
"com.apple.Maps" with url "maps:<private>" from lsd:1878
on behalf of DadCost1a:7527.

2026-07-02 17:26:24.805110
SpringBoard[36]: Received untrusted open application request for
"com.apple.Maps" from <FBApplicationProcess ... app<au.com.philk.DadCost1aus>:7527>

2026-07-02 17:26:24.805803
SpringBoard[36]: Open "com.apple.Maps" request from lsd:1878 failed with error:
FBSOpenApplicationServiceErrorDomain; code: 1 ("RequestDenied")

Reason:
Application au.com.philk.DadCost1aus is neither visible nor entitled,
so may not perform un-trusted user actions.

Underlying:
FBSOpenApplicationErrorDomain; code: 3 ("Security")

2026-07-02 17:26:24.806815
lsd[1878]: Error handling open request for com.apple.Maps:
FBSOpenApplicationServiceErrorDomain; code: 4 ("InvalidRequest")

Underlying:
FBSOpenApplicationErrorDomain; code: 3 ("Security");
Request is not trusted.

2026-07-02 17:26:24.807283
DadCost1a[7527]: Failed to open URL <private>:
Error Domain=LSApplicationWorkspaceErrorDomain Code=115

2026-07-02 17:26:24.807486
DadCost1a[7527]: Requesting present template <private> animated 1

The last line is DadCost1a presenting the fallback "Unable to open Maps" CarPlay alert.

Repeated Denials in the Same Sysdiagnose

The exact same SpringBoard denial appears multiple times in the July 2 sysdiagnose:

2026-07-02 15:14:55
2026-07-02 15:15:01
2026-07-02 15:15:40
2026-07-02 15:15:42
2026-07-02 15:18:43
2026-07-02 15:18:45
2026-07-02 16:57:57
2026-07-02 17:13:36
2026-07-02 17:26:22
2026-07-02 17:26:24

All of these failures use the same reason:

Application au.com.philk.DadCost1aus is neither visible nor entitled,
so may not perform un-trusted user actions.

The same sysdiagnose also contains successful Maps open requests for the same app and Maps process, including:

2026-07-02 15:14:34
2026-07-02 15:14:50
2026-07-02 15:15:35
2026-07-02 15:18:56
2026-07-02 16:57:54
2026-07-02 17:13:31
2026-07-02 17:26:13
2026-07-02 17:26:17

This supports an intermittent trust/visibility/routing problem rather than a permanent entitlement, Maps installation, or URL construction failure.

Sometimes after seeing the failure message I can tap on the Maps app icon in CarPlay, see the maps interface, return to DadCost1a and the station row that failed will now succeed when tapped again.

Visibility and Routing State Around Failure

The visible CarPlay UI is hosted through CarPlayTemplateUIHost.

At 17:26:04, CarPlay is updating and forwarding DadCost1a's hosted CarPlay template scene:

2026-07-02 17:26:04.931693
CarPlay[689]: Updating user interface style ... for scene:
com.apple.DashBoard.scene-workspace.default0:Car[2-89]:
com.apple.CarPlayTemplateUIHost:au.com.philk.DadCost1aus

2026-07-02 17:26:04.943357
CarPlayTemplateUIHost[7789]:
[kCARTemplateUIAppWorkspaceIdentifier(FBSceneManager):Car[2-89]:
au.com.philk.DadCost1aus] Scene activity mode did change: support (transient).

2026-07-02 17:26:04.943387
CarPlayTemplateUIHost[7789]:
[app<au.com.philk.DadCost1aus>:7527] Workspace assertion state did change:
BackgroundActive (acquireAssertion = YES).

2026-07-02 17:26:04.943600
runningboardd[35]: Acquiring assertion targeting
[app<au.com.philk.DadCost1aus>:7527] from originator
[app<com.apple.CarPlayTemplateUIHost>:7789] with description
"FBWorkspace (BackgroundActive)"

2026-07-02 17:26:04.944821
runningboardd[35]: Calculated state for app<au.com.philk.DadCost1aus>:7527:
running-active (role: NonUserInteractive)

2026-07-02 17:26:04.944923
runningboardd[35]: [app<au.com.philk.DadCost1aus>:7527]
Set darwin role to: NonUserInteractive

SpringBoard then sees DadCost1a as not visible/background:

2026-07-02 17:26:04.945164
SpringBoard[36]: Received state update for 7527
(app<au.com.philk.DadCost1aus>, running-active-NotVisible)

2026-07-02 17:26:04.945573
SpringBoard[36]: Application process state changed for au.com.philk.DadCost1aus:
taskState: Running; visibility: Background

Shortly afterward, DadCost1a is suspended/not visible from the SpringBoard/RunningBoard perspective:

2026-07-02 17:26:05.046146
SpringBoard[36]: Received state update for 7527
(app<au.com.philk.DadCost1aus>, running-suspended-NotVisible)

2026-07-02 17:26:05.047372
SpringBoard[36]: Application process state changed for au.com.philk.DadCost1aus:
taskState: Suspended; visibility: Background

The key observation is that the successful handoffs at 17:26:13 and 17:26:17 are routed through CarPlay[689], while the failed handoffs at 17:26:22 and 17:26:24 are routed through SpringBoard[36] and denied using SpringBoard's main-workspace visibility/trust decision.

Why This Does Not Look Like a Data or Maps Availability Issue

The logs show:

  • Maps is installed and resolvable by LaunchServices.
  • Maps process 7790 is running around both successful and failed handoffs.
  • Earlier handoffs from the same DadCost1a process succeed in the same CarPlay session.
  • The failed handoffs reach SpringBoard and are explicitly denied for security/trust reasons.
  • There is no DadCost1a crash or Maps crash tied to the failed handoff.
  • DadCost1a validates the station coordinate before building the URL. If the coordinate were invalid, the code would present the local fallback alert without reaching lsd or SpringBoard.
  • The failed path receives LSApplicationWorkspaceErrorDomain Code=115, which is consistent with the SpringBoard/FrontBoard denial and not with malformed station data.

Questions for DTS

  1. For a CarPlay fueling app with com.apple.developer.carplay-fueling, what is the recommended API for handing a selected coordinate to Apple Maps from CarPlay?
  2. If the raw maps: URL approach is supported, why would successful requests from the same process be handled by CarPlay[689], while later requests are handled by SpringBoard[36] and denied?
  3. Does calling the CPListItem handler completion immediately after initiating scene.open(...) affect the trusted user-action window? Should the app defer the CPListItem completion until the Maps-open completion handler runs?
  4. If this denial is not expected, is this likely an iOS CarPlay / SpringBoard / FrontBoard bug where the app is user-visible in CarPlay but considered background/not visible by SpringBoard's main workspace?
CarPlay handoff to MapKit fails on n+1 attempts
 
 
Q