Skip to content

chronod: a Live Activity ended right after a restart keeps reloading with "Could not find a descriptor"

Generated from repros/ios-chronod-dismissed-activity-reload-loop/README.md when the site was built at 7aa23e2.

hosts/apple/Sources/SurfacePublisher.swift used to end every Live Activity an earlier run left, with .immediate, as soon as the app launched. After a restart that triggers this bug. Now the app keeps a leftover activity when the page still has an activity surface. When the page has none, the app ends the leftover with .after(now + 15 s): an ended activity keeps loading until it is dismissed, so the scenes SpringBoard restores get their content before the activity goes away.

repro.sh [UDID] builds a two-file app with a widget extension (App.swift, Widgets.swift, Attributes.swift), and then runs these steps on an iOS simulator. It exits 1 while the bug reproduces. --loop also waits 12 minutes for chronod’s first scheduled retry.

  1. Launch the app with -start. It requests a Live Activity (ActivityConfiguration showing a timer).
  2. Restart the device (simctl shutdown, simctl boot).
  3. Launch the app as soon as the device is up. On launch it ends every activity in Activity<ReproAttributes>.activities with dismissalPolicy: .immediate.

Found with Xcode 27.0 (27A266a), iOS 27.0 (24A434) simulator, iPhone 17. It has not been tried on a device. On a device the same order happens when an app launches right after a reboot. It probably also happens right after an app update, but that has not been tried.

The activity is ended and dismissed. chronod drops it, the same as when the app ends an activity without a restart first: chronod logs Activity state changed to: dismissed and Dropping subscription for dismissed activity.

About ten seconds after the app ends the activity, chronod tries to load its content, fails, and schedules a retry that keeps repeating:

22:09:49.633 chronod [com.apple.chrono:activity] Adding descriptor for changed activity: E779BAE1-…; state: active
22:10:00.184 chronod [com.apple.chrono:activity.store] Could not find a descriptor for E779BAE1-…
22:10:00.188 chronod [com.apple.chrono:activity.store] [dev.niso.repro.liveactivity::dev.niso.repro.liveactivity.widgets:Attributes type: ReproAttributes:E779BAE1-…] reload: failed during preparation.
- [w:fix-374.00-h:dyn-64.00-160.00-cr:23.50000-s:1.00000-….fam:medium]: ChronoCore.PlatterContentError(code: Activity had no descriptor., …)
22:10:00.220 chronod [com.apple.chrono:activity] [E779BAE1-…] Ending activity because it failed to load its initial content repeatedly.

liveactivitiesd has already discarded the activity, so chronod’s request to end it gets Received an update for an activity with no subscribers. From then on, every ~10.7 minutes chronod launches the widget extension and repeats the same lines for the same activity id (Reload failed; next attempt due at …, scheduledRetry(externalRequest(… reason: payloadChanged …))), until chronod restarts. Nothing shows on screen, because SpringBoard did dismiss the activity. The visible cost is the repeated extension launches and error logs.

  1. Boot. liveactivitiesd: Restored activities: <id>. SpringBoard registers new ActivityUIServices and SystemAperture scenes for it. chronod: Adding descriptor for changed activity: <id>; state: active.
  2. App launch, about 3 s later. The app ends the activity with .immediate. liveactivitiesd: Ending activity <id> for XPC participant content source process(<app>), then Activity discarded. chronod’s ActivityKit client logs Activity dismissed: <id> and Removing dismissed ephemeral activity request. But chronod has no subscription for the activity yet, so it logs neither Activity state changed to: dismissed nor Dropping subscription.
  3. About 9 s later, the scene SpringBoard restored in step 1 subscribes: Subscribing to activity: [<app>::<extension>:Attributes type: …:<id>]. chronod treats this as an initial load. It asks ActivityKit for the activity’s descriptor (Fetched descriptors: [], since the activity is gone) and fails with Could not find a descriptor three times.
  4. chronod gives up (Ending activity because it failed to load its initial content repeatedly). But the reload task stays scheduled, and it retries the dead activity every ~10.7 minutes.

The extension’s own Live Activity descriptor is fine: chronod’s database (Library/chronod/chrono.sql, table Descriptors) holds a CHSLiveActivityDescriptor with the right attributesType, and new activities load.

Without the restart, ending the activity at launch is clean. With the restart, ending it with .after(now + 15 s) instead of .immediate is also clean: the restored scene loads the ended activity’s content (Content load successful), and when it is dismissed chronod logs Dropping subscription for dismissed activity.

chronod keeps reloading a Live Activity that was ended before SpringBoard’s restored scene subscribed to it

After a restart, if an app ends a restored Live Activity with .immediate before SpringBoard’s restored activity scenes subscribe, chronod later starts an initial content load for the dead activity. It logs “Could not find a descriptor for ” / “Activity had no descriptor”, tries to end the activity, and then retries every ~10.7 minutes, each time launching the widget extension, until chronod restarts. The attached project reproduces it on the iOS 27.0 simulator (Xcode 27.0): repro.sh starts an activity, restarts the simulator, launches the app (which ends leftover activities at launch), and prints chronod’s log.

Expected: chronod drops a dismissed activity even when no subscription exists yet, and ignores later subscriptions to it, as it does when no restart happened.

Not filed yet.