WatchOS HealthKit HKObserverQuery crashes in background

I have a watchOS app with a connected iOS app using Swift and SwiftUI. The watchOS app should read heart rate date in the background using HKOberserQuery and enableBackgroundDelivery(), send the data to the iPhone app via WCSession. The iPhone app then sends the data to a Firebase project. The issue I am facing now it that the app with the HKObserverQuery works fine when the app is in the foreground, but when the app runs in the background, the observer query gets triggered for the first time (after one hour), but then always get terminated from the watchdog timeout with the following error message: CSLHandleBackgroundHealthKitQueryAction scene-create watchdog transgression: app<app.nanacare.nanacare.nanaCareHealthSync.watchkitapp((null))>:14451 exhausted real (wall clock) time allowance of 15.00 seconds

I am using Xcode 16.3 on MacOS 15.4 The App is running on iOS 18.4 and watchOS 11.4

What is the reason for this this issue? I only do a simple SampleQuery to fetch the latest heart rate data inside the HKObserverQuery and then call the completionHandler. The query itself takes less than one second.

Or is there a better approach to read continuously heart rate data from healthKit in the background on watchOS? I don't have an active workout session, and I don't need all heart rate data. Once every 15 minutes or so would be enough.

Answered by DTS Engineer in 835536022

In the use case you described, HKOberserQuery + enableBackgroundDelivery is the right way to go.

When you use enableBackgroundDelivery in watchOS though, the background updates share a budget with WKApplicationRefreshBackgroundTask tasks. Your app can receive four updates (or background app refresh tasks) an hour, as long as it has a complication on the active watch face. This is documented here.

When your app is woken up to run the update handler of the observer query, watchOS assigns a period of time for your app to run in the background. The error message you provided indicates that your app ran out of the time allowance.

In the case where you "do a simple SampleQuery to fetch the latest heart rate data inside the HKObserverQuery," 15 seconds is quite enough, and so I am wondering if you have any code path that failed to call the completionHandler passed to the updateHandler of your observer query, which had watchOS believe that your observer query was running in the background for ever. You can double check if that is the case.

The other factor is Watch Connectivity. Your watchOS app uses WCSession to send data to your companion iOS app when running in the background. The execution time is counted against the budget as well. For debugging purpose, you can temporarily disable the data transfer, and check if the crash still happens.

If you've reviewed all your code paths, and are confident that your code should not hit the budget, I’d suggest that you file a feedback report with the following information for the watchOS folks to investigate:

  • The relevant code snippets, or even better, a minimal sample that reproduces the issue.
  • A full crash report.
  • A co-sysdiagnose. See the Instructions here for how to capture one.

If you do so, please share your report ID here for folks to track.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

In the use case you described, HKOberserQuery + enableBackgroundDelivery is the right way to go.

When you use enableBackgroundDelivery in watchOS though, the background updates share a budget with WKApplicationRefreshBackgroundTask tasks. Your app can receive four updates (or background app refresh tasks) an hour, as long as it has a complication on the active watch face. This is documented here.

When your app is woken up to run the update handler of the observer query, watchOS assigns a period of time for your app to run in the background. The error message you provided indicates that your app ran out of the time allowance.

In the case where you "do a simple SampleQuery to fetch the latest heart rate data inside the HKObserverQuery," 15 seconds is quite enough, and so I am wondering if you have any code path that failed to call the completionHandler passed to the updateHandler of your observer query, which had watchOS believe that your observer query was running in the background for ever. You can double check if that is the case.

The other factor is Watch Connectivity. Your watchOS app uses WCSession to send data to your companion iOS app when running in the background. The execution time is counted against the budget as well. For debugging purpose, you can temporarily disable the data transfer, and check if the crash still happens.

If you've reviewed all your code paths, and are confident that your code should not hit the budget, I’d suggest that you file a feedback report with the following information for the watchOS folks to investigate:

  • The relevant code snippets, or even better, a minimal sample that reproduces the issue.
  • A full crash report.
  • A co-sysdiagnose. See the Instructions here for how to capture one.

If you do so, please share your report ID here for folks to track.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Hi Ziqiao, thanks for the reply. I have already tried disabling the data transfer to the iPhone via wcsession, but the same error still occurs. Is there any sample code / project from apple about how to implement this? One other question came to my mind: You said that my app can receive 4 updates per hour as long as it has a complication on the active watch face. I don't have a complication for the app. Does this mean that the app can only receive up to 1 update per hour from the HKObserverQuery? What would happen if I also want to get updates from other data types, not just heart rate. For example in addition also step count, calories, blood oxygen. If I would register an HKObserverQuery for each of these 4 data types with a frequency of hourly. If all 4 HKObserverQueries "wake up" every hour, would that already be 4 background updates per hour? Which would be too much already without a complication. Thanks & best regards, Tobias

I have the same problem, I am monitoring HRV data using HKOberserQuery + enableBackgroundDelivery with HKUpdateFrequency.hourly . in HKOberserQuery i only gets the latest one HRV sample.

I make sure HKObserverQueryCompletionHandler is called correctly, but I see a lot of CSLHandleBackgroundHealthKitQueryAction crash log on the watch logs, and I'm seeing the same logs on other apps too!

@tobiasbader:

Does this mean that the app can only receive up to 1 update per hour from the HKObserverQuery? What would happen if I also want to get updates from other data types, not just heart rate.

When you use HKOberserQuery with background delivery enabled to monitor some data points, assuming that your app is suspended and the first change happens, the system will wake up your app and allow it to run for a short period of time. If a second change comes within the time allowance, your app isn't in the suspended state, and so no waking-up is needed; otherwise, your app will be suspended again shortly after you call the completion handler (HKObserverQueryCompletionHandler) to signal that you've handled the first change. If the completion handler is not called, the system will crash your app after the time allowance, which is what your error message indicates.

@blacksun

I make sure HKObserverQueryCompletionHandler is called correctly, but I see a lot of CSLHandleBackgroundHealthKitQueryAction crash log on the watch logs, and I'm seeing the same logs on other apps too!

In this case, I'd suggest that you start with filing a feedback report, as mentioned in my previous post.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Hello Ziqiao, thanks for the information. But you didn't answer my questions from the previous comment. Therefore could you please answer these questions?

  1. Is there any sample code / project from apple about how to implement background HealthKit queries on WatchOS?
  2. How many updates per hour can a WatchOS app receive in the background if there is no complication on the active watch face?
  3. If I have observer queries registered for 4 different data types, each trigger of these observer queries would count as one separate wake up if they don't occur at the same time or directly after each other within the time allowance of the first one, correct?
  4. Is it better / recommended to read HealthKit data in the background on iOS instead of WatchOS? On iOS I discovered the problem that I cannot read heart rate data in the background if the iPhone is locked.
  5. Is there a way to read heart rate data in the background on iOS if the iPhone is locked?
  6. Is there a list of data types from HealthKit which ones can be read in the background if the iPhone is locked and which ones cannot?

Thanks a lot! Tobias

Hello Ziqiao, would you please reply to Tobias' points. Thank you in advance! Manuel

I have been pretty swamped since my last response. Now that WWDC was done, I'd like to continue this conversation. Hopefully this's not too late.

Is there any sample code / project from apple about how to implement background HealthKit queries on WatchOS?

There is no Apple sample that focuses on this topic so far.

How many updates per hour can a WatchOS app receive in the background if there is no complication on the active watch face?

What I can say is that, if your app has an active complication, you can expect that it receives four background updates per hour. If your app doesn't, apps that have an active complication will get prioritized. Depending on how many prioritized apps the system currently has, your app may get less than 4 times per hour.

So the answer is really that it depends on the system situation. Your app should be prepared for the situation that you get less updates.

If I have observer queries registered for 4 different data types, each trigger of these observer queries would count as one separate wake up if they don't occur at the same time or directly after each other within the time allowance of the first one, correct?

Yes, that's my understanding. Every wake-up is counted against the quota.

Is it better / recommended to read HealthKit data in the background on iOS instead of WatchOS?

I'd say that reading Health data from your watchOS app is not better or worse than reading from your iOS app. It really depends on where you need the data – If your watchOS app needs to process Health data, it is absolutely fine to read the data directly from the watchOS HealthKit store.

Note though that the watchOS HealthKit store is a subset of the iOS one. See this post for more information.

On iOS I discovered the problem that I cannot read heart rate data in the background if the iPhone is locked. Is there a way to read heart rate data in the background on iOS if the iPhone is locked? Is there a list of data types from HealthKit which ones can be read in the background if the iPhone is locked and which ones cannot?

An iOS app can't read the HealthKit store when the iPhone is locked, and this is as-designed to better protect the user privacy.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Hello Ziqiao, thanks a lot for getting back to me and for answering all my questions from my previous reply. In the meantime I also created a bug report via the Apple Feedback app and was in contact with Apple that way. After some improvement suggestions of my code, which unfortunately didn't solve the issue, they suggested to create a post here in the developer forum. So unfortunately after many tries and many ways to reach out to Apple to get this issue fixed, I still don't have a solution and the Watch app is still crashing in the background. I have attached the Swift file with the latest version of the coding for the Watch app, which includes all the suggestions I have received so far but still does not work. Could you please take a look at it again and provide me with a fixed version of this code which does not crash any more?

Thanks a lot.

@DTS Engineer

Yes, we would greatly appreciate if Apple could look into this issue. This crash has already generated tens of thousands of crash reports in our app's backend, and we've also observed several similar crash logs in other health-related apps on Apple Watch. Thank you very much for your attention to this matter.


-------------------------------------
Translated Report (Full Report Below)
-------------------------------------

Incident Identifier: 74A69084-446E-461C-9401-FC97A426180B
CrashReporter Key:   7d3c0ace745a10af53054a01d91601a47d39d41e
Hardware Model:      Watch7,5
Identifier:          com.abc.watchkitapp
Version:             2.1.0 (967)
Code Type:           ARM-64 (Native)
Role:                Non UI
Parent Process:      launchd [1]
Coalition:           com.abc.watchkitapp [633]

Date/Time:           2025-07-02 20:38:25.6084 +0800
Launch Time:         2025-07-02 19:01:28.0292 +0800
OS Version:          Watch OS 26.0 (23R5296f)
Release Type:        User
Baseband Version:    
Report Version:      104

Exception Type:  EXC_CRASH (SIGKILL)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Termination Reason: CAROUSEL 3306925314 
CSLHandleBackgroundHealthKitQueryAction watchdog transgression: app< com.abc.watchkitapp((null))>:20784 exhausted real (wall clock) time allowance of 15.00 seconds
<FBApplicationProcess: 0x3d81a8480; app< com.abc.watchkitapp>:20784(vE5A4)> Elapsed total CPU time (seconds): 7.600 (user 4.670, system 2.930), 25% CPU
Elapsed application CPU time (seconds): 4.427, 14% CPU, lastUpdate 2025-07-02 12:38:10 +0000
<0x0> watchdog transgression. Exhausted wall time allowance of 15.00 seconds.
WatchOS HealthKit HKObserverQuery crashes in background
 
 
Q