---
title: "HealthKit Authorization Denied: What It Means and What It Hides"
canonical: "https://aifitnessapi.com/fix/healthkit-authorization-denied"
cluster: "Troubleshooting"
primary_query: "healthkit authorization denied"
last_reviewed: "2026-08-11"
description: "sharingDenied covers writes only — Apple never reveals a denied read. What HealthKit confirms, what it hides by design, and how to route users to fix it."
publisher: "AIFitnessAPI — independent, not sponsored"
cite_as: "\"HealthKit Authorization Denied: What It Means and What It Hides\", AIFitnessAPI, https://aifitnessapi.com/fix/healthkit-authorization-denied"
---

# HealthKit Authorization Denied: What It Means and What It Hides

> In HealthKit, a denied authorization is only visible on the write side. The status returned by authorizationStatus(for:) — notDetermined, sharingDenied, or sharingAuthorized — describes permission to save data, and Apple documents that your app cannot determine whether a user granted permission to read data, because a denied read simply looks like an empty store. If your app has share permission but not read permission, Apple states you see only the samples your own app wrote, and data from other sources stays hidden. The single exception is limited authorization: when someone grants a recent window of history instead of their full history, getEarliestAuthorizedSampleDate reveals that date, and Apple calls it the only authorization state your app can positively identify.

- Canonical: https://aifitnessapi.com/fix/healthkit-authorization-denied
- Last reviewed: 2026-08-11
- Publisher: AIFitnessAPI (https://aifitnessapi.com) — independent, not sponsored
- Cite as: "HealthKit Authorization Denied: What It Means and What It Hides", AIFitnessAPI, https://aifitnessapi.com/fix/healthkit-authorization-denied

---

There are two ways people arrive at "HealthKit authorization denied," and they are opposite mistakes about the same API. One developer calls `authorizationStatus(for:)`, gets `.sharingDenied`, and disables the whole feature — including the reads, which that status never described. The other gets an empty array back, concludes the user denied them, and shows a "permission required" screen to someone who simply has no samples of that type. Untangling this starts with reading Apple's own description of what the status call covers.

## What authorizationStatus(for:) actually reports

Apple documents the method plainly: "This method checks the authorization status for saving data to the HealthKit store." Saving. Not reading. The three values it can return are all about the share side:

| Case | Apple's description |
| --- | --- |
| `.notDetermined` | "The user has not yet chosen to authorize access to the specified data type." |
| `.sharingDenied` | "The user has explicitly denied your app permission to save data of the specified type." |
| `.sharingAuthorized` | "The user has explicitly authorized your app to save data of the specified type." |

Even the enum name carries the warning: `sharing`, not `access`. Apple's overview for `HKAuthorizationStatus` says the same thing from the other direction — "This status indicates whether the user has authorized your app to save data of the given type."

So `.sharingDenied` is a complete answer to one question ("can I write this type?") and no answer at all to the other. Your reads may be fully granted alongside it. Read and share are separate permissions per type; Apple notes that "each data type has two separate permissions, one to read it and one to share it," and the permission sheet lets people toggle them independently within each category.

## Read denial is invisible, and Apple says so explicitly

This is the part that makes the symptom so confusing, and it is deliberate. Apple documents on `authorizationStatus(for:)`:

> To help prevent possible leaks of sensitive health information, your app cannot determine whether or not a user has granted permission to read data. If you are not given permission, it simply appears as if there is no data of the requested type in the HealthKit store. If your app is given share permission but not read permission, you see only the data that your app has written to the store. Data from other sources remains hidden.

The authorization article repeats it: "your app doesn't know whether someone granted or denied permission to read data. If they denied permission, attempts to read data from HealthKit return only samples that your app successfully saved to the HealthKit store."

That last clause is the detail people miss. A denied read is not an empty store — it is a store filtered down to *your own writes*. An app that saves workouts and reads them back will see its own data flowing perfectly while every other source stays invisible, which looks exactly like a working integration until a user with an Apple Watch complains.

## The one authorization state you can positively identify

There is a single exception, and it is newer than most HealthKit code. Apple documents that after the data-type screen, "a second screen prompts them to choose how much historical data to grant your app, either a recent limited window or their full history." When someone picks the limited window, your app is in a limited authorization state for those types, and that state *is* observable.

Call `getEarliestAuthorizedSampleDate(for:completion:)` after requesting authorization — Apple describes it as returning "the earliest date that the person permits your app to read samples for the given data types." The framing in Apple's article is worth quoting because it settles the whole question:

> HealthKit intentionally prevents your app from distinguishing between full access and denied access to specific types; both cases return no entry in the result dictionary. Limited authorization is the only authorization state your app can positively identify.

No entry in the dictionary means either full access or denial. You still cannot tell those apart. What you gain is the ability to stop treating a short history as a data gap: Apple warns against "interpreting the absence of older samples as evidence they don't exist; the person's history may extend before the date the method returns," and shows passing the returned `startDate` into your query predicate rather than reaching for `.distantPast`.

```swift
let types: Set<HKObjectType> = [HKQuantityType(.stepCount)]
let authorizationDates = try await store.earliestAuthorizedSampleDate(for: types)
let queryStartDate = authorizationDates[HKQuantityType(.stepCount)] ?? .distantPast
```

Apple also suggests that if your app makes inferences on partial data, you consider telling people that granting full access improves the experience.

## What getRequestStatusForAuthorization does and does not tell you

This is the other method people reach for when they want a grant check, and it answers a different question again. Apple's abstract: it "indicates whether the system presents the user with a permission sheet if your app requests authorization for the provided types." Its three results are about the *prompt*, not the *outcome*:

- `.shouldRequest` — "The application has not yet requested authorization for all the specified data types."
- `.unnecessary` — "The application has already requested authorization for all the specified data types."
- `.unknown` — "The authorization request status could not be determined because an error occurred."

`.unnecessary` means "asking again would show nothing," which is emphatically not "you were granted access." Treat it as a UI hint for whether to show your own pre-permission explainer, and nothing more.

It also explains a bug report you will eventually receive: calling `requestAuthorization(toShare:read:)` a second time appears to do nothing. Apple documents exactly that behaviour — "if the user has already chosen to grant or prohibit access to all of the types specified, HealthKit returns the request without prompting the user." You cannot re-prompt your way out of a denial.

## Symptom to cause

| Symptom | What it actually means | What to do |
| --- | --- | --- |
| `authorizationStatus(for:)` returns `.sharingDenied` | Writes for that type are denied. Reads are unaffected and unknown. | Disable saving for that type only; keep reading |
| `authorizationStatus(for:)` returns `.notDetermined` | You never requested authorization, or never for this type | Call `requestAuthorization(toShare:read:)` |
| Query returns empty, but your own saved samples come back | Read permission is denied for that type | Route the user to Settings or the Health app |
| Query returns empty including your own writes | No data of that type exists, or plumbing is broken | Isolate with a write-then-read test |
| Samples appear, but history starts recently | Limited authorization, not a gap | Use the date from `getEarliestAuthorizedSampleDate` |
| `requestAuthorization` shows no sheet | Every requested type is already decided | Send the user to Settings; do not re-prompt |
| Save fails with `errorAuthorizationDenied` | The user explicitly denied write for that type | Stop saving; surface it in your UI honestly |
| Save fails with `errorAuthorizationNotDetermined` | You never asked | Request authorization before saving |

## Denied writes, unlike denied reads, have real error codes

Because the share side is observable, it also fails loudly. Apple's guidance is to check `authorizationStatus(for:)` before attempting a save, and documents the two failures you get if you skip that check: `errorAuthorizationNotDetermined` ("the app hasn't yet asked the user for the authorization required to complete the task") and `errorAuthorizationDenied` ("the user hasn't given the app permission to save data").

If your product treats "denied" as a single state, this asymmetry is the thing to encode instead: writes give you a status and an error code, reads give you neither.

## The Info.plist keys that turn a permission bug into a crash

Two usage-description keys govern the two sides, and Apple is unambiguous about what happens without them: "You must set the usage keys, or your app will crash when you request authorization."

- `NSHealthShareUsageDescription` — Apple describes it as "a message that explains to people why the app requests permission to read samples from the HealthKit store." Required for reading.
- `NSHealthUpdateUsageDescription` — "a message to the user that explains why the app requested permission to save samples to the HealthKit store." Required for writing.

For projects created with Xcode 13 or later, Apple says to set these in the Target Properties list on the app's Info tab; older projects set them in the information property list. If your "authorization denied" report is really a crash at the request call, this is where to look first, and the [HealthKit integration guide](/integrate/healthkit) covers the surrounding setup.

## The correct UX pattern: you cannot fix this in code

There is no API to toggle a permission on a user's behalf, so every honest recovery path ends in the system UI. Apple: "a person can change the permissions for your app at any time using either the Settings or Health app. After prompting for HealthKit authorization, your app appears in the Health app's Sources tab, even if the person didn't allow permission to read and share data."

That last clause is the useful one to put in your copy. Your app is listed under Sources whether or not anything was granted, so "open the Health app, find this app under Sources, and turn on the types you want shared" is instructions a user can actually follow. Two rules that follow from everything above:

- **Never gate your UI on read-authorization status**, because there isn't one. Run the query, render what comes back, and treat empty as a legitimate state rather than an error — the same discipline that makes an empty read a first-class [HealthKit test case](/test/healthkit-integration).
- **Do not nag.** Re-calling `requestAuthorization` after a decision shows no sheet at all, so a "grant access" button that silently does nothing is worse than a line of text pointing at the Health app.

## Edge cases that look like denial but are not

- **Apple Vision Pro Guest User sessions.** Apple documents that in a guest session the guest can read data the owner already authorized but cannot authorize additional types, the authorization sheet is not displayed so "any attempt to request authorization for HealthKit data types during a Guest User session fails silently," and writes fail with `errorNotPermissibleForGuestUserMode` (or `errorHealthDataRestricted` on apps running in iOS 17). Critically, `authorizationStatus(for:)` still reports the owner's grant, so your status check and your save disagree. Apple's advice is to silently ignore the error for passive or periodic saves, and only surface an alert when the guest took an action that obviously implies saving.
- **Managed devices.** `errorHealthDataRestricted` also signals that "a Mobile Device Management (MDM) profile restricts the use of HealthKit on this device" — an organisational policy, not a user choice.
- **Required clinical record types.** If you declare types under the `NSHealthRequiredReadAuthorizationTypeIdentifiers` key, Apple says to specify three or more, and that denying any of them fails authorization with `errorRequiredAuthorizationDenied` — "the system doesn't tell your app which record types the person denied access to."

## Where to go next

If your immediate problem is an empty query rather than the authorization model itself, the write-then-read isolation sequence is laid out in [HealthKit returning no data](/fix/healthkit-no-data). If you are designing a pipeline that has to survive this ambiguity at scale — distinguishing gaps from denials across many users — see [handling missing data and gaps](/architecture/missing-data-and-gaps). And if you are shipping the same feature on Android, Health Connect makes the opposite trade: granted read permissions are queryable, and a missing one surfaces as an exception on the read rather than as silence. The two models are set side by side in [Apple HealthKit vs Google Health Connect](/fitness-apis/apple-healthkit-vs-google-health-connect).

## FAQ

### Does sharingDenied mean my HealthKit reads will fail too?

No. Apple documents authorizationStatus(for:) as checking the status for saving data to the HealthKit store, and the enum describes whether the user authorized your app to save data of the given type. Read and share are separate permissions on every type, so a user can deny writes while allowing reads. Disable saving for that type and keep running your queries.

[Permalink](https://aifitnessapi.com/fix/healthkit-authorization-denied#faq-1)

### What does getRequestStatusForAuthorization actually tell me?

Apple describes it as indicating whether the system presents a permission sheet if your app requests authorization for the provided types. Its results are shouldRequest, meaning you have not yet requested all the specified types, unnecessary, meaning you already have, and unknown, meaning an error occurred. Unnecessary means asking again would show nothing — it is not confirmation that access was granted.

[Permalink](https://aifitnessapi.com/fix/healthkit-authorization-denied#faq-2)

### Why does calling requestAuthorization again show no permission sheet?

Apple documents that if the user has already chosen to grant or prohibit access to all of the types specified, HealthKit returns the request without prompting. A second call is a no-op once every requested type has been decided, so a grant-access button that re-requests will silently do nothing. Point the user at Settings or the Health app instead.

[Permalink](https://aifitnessapi.com/fix/healthkit-authorization-denied#faq-3)

### How do I know if a user granted only a limited window of health history?

Apple's authorization sheet includes a second screen where people choose between a recent limited window and their full history. Call getEarliestAuthorizedSampleDate(for:completion:) to get the earliest date you may read for each type and pass it into your query. Apple calls limited authorization the only authorization state your app can positively identify, and warns against treating the absence of older samples as proof they do not exist.

[Permalink](https://aifitnessapi.com/fix/healthkit-authorization-denied#faq-4)

### Why do HealthKit writes fail on Vision Pro when my status check says authorized?

In a Guest User session, Apple documents that an app's permissions do not change, so authorizationStatus(for:) still reports the owner's grant while any attempt to save fails with errorNotPermissibleForGuestUserMode, or errorHealthDataRestricted on apps running in iOS 17. The authorization sheet is not displayed either, so requests during a guest session fail silently. Apple suggests ignoring the error for passive or periodic saves and only alerting when the guest took an action that obviously implies saving.

[Permalink](https://aifitnessapi.com/fix/healthkit-authorization-denied#faq-5)
