---
title: "Health Connect SecurityException: The Permission Was Not Granted"
canonical: "https://aifitnessapi.com/fix/health-connect-securityexception"
cluster: "Troubleshooting"
primary_query: "health connect securityexception"
last_reviewed: "2026-10-03"
description: "Google says Health Connect throws SecurityException when a request needs a permission that isn't granted. The causes, ranked, and the documented fix."
publisher: "AIFitnessAPI — independent, not sponsored"
cite_as: "\"Health Connect SecurityException: The Permission Was Not Granted\", AIFitnessAPI, https://aifitnessapi.com/fix/health-connect-securityexception"
---

# Health Connect SecurityException: The Permission Was Not Granted

> Health Connect throws SecurityException when a call needs a permission your app does not currently hold. Google's exception table describes it as issues "when the requests require permissions that aren't granted". The usual causes are a permission that was declared but never granted, one the user has since revoked, or one missing from the manifest. The fix is to call getGrantedPermissions before every read or write, request anything missing with PermissionController.createRequestPermissionResultContract, and treat the exception as "not allowed to look", never as "no data".

- Canonical: https://aifitnessapi.com/fix/health-connect-securityexception
- Last reviewed: 2026-10-03
- Publisher: AIFitnessAPI (https://aifitnessapi.com) — independent, not sponsored
- Cite as: "Health Connect SecurityException: The Permission Was Not Granted", AIFitnessAPI, https://aifitnessapi.com/fix/health-connect-securityexception

---

Your Android app calls `readRecords`, `insertRecords`, `aggregate` or `getChanges`, and instead of data you get `java.lang.SecurityException`. Health Connect is not telling you the data is missing. It is telling you the call needed a permission your app does not hold at that moment. Google documents exactly one meaning for it, and almost every fix comes down to checking the granted set before you call.

## What Google documents

Google's read-data and write-data guides carry the same exception table. The `SecurityException` row reads, verbatim:

> There are issues encountered when the requests require permissions that aren't granted.

The recommended best practice in the same row: "make sure that you've declared use of Health Connect data types for your published app. Also, you must declare Health Connect permissions in the manifest file and in your activity." (Both guides fetched October 3, 2026: [read data](https://developer.android.com/health-and-fitness/health-connect/read-data), [write data](https://developer.android.com/health-and-fitness/health-connect/write-data).)

Android's Jetpack [`HealthConnectClient` reference](https://developer.android.com/reference/kotlin/androidx/health/connect/client/HealthConnectClient) lists `SecurityException` under Throws for `readRecords`, `readRecord`, `insertRecords`, `aggregate`, `deleteRecords`, `getChanges` and `getChangesToken`, each time with the same gloss: "For requests with unpermitted access." The one place it names a specific permission is the medical-records API: "Caller must hold PERMISSION_WRITE_MEDICAL_DATA in order to call this API, otherwise a SecurityException will be thrown." [Health Connect error codes](/error-codes/health-connect) sets `SecurityException` beside the other exception types the `HealthConnectClient` reference documents, with how many methods each appears on.

## Where the HealthConnectException in your stack trace comes from

On Android 14 and later Health Connect is part of the system, and the platform API reports failures as `android.health.connect.HealthConnectException` with an error code. Android's [platform reference](https://developer.android.com/reference/android/health/connect/HealthConnectException) describes `ERROR_SECURITY` as "The caller doesn't have the correct permissions for this call. This error may be considered similar to SecurityException." The Jetpack library's own source ([`ExceptionConverter.kt`](https://github.com/androidx/androidx/blob/androidx-main/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/ExceptionConverter.kt), read October 3, 2026) converts that code into `SecurityException(this)` in its platform code path, which requires API 34. So on those devices the `SecurityException` you catch carries the platform exception as its cause. That is internal library code, not a documented contract, so read the cause for logging and never branch on its message text.

## Most likely causes (ranked)

The ranking is our judgement. Each cause is tied to what Google documents.

1. **Declared, never granted, or since revoked.** The manifest entry makes a permission requestable. It does not grant it. Google's get-started guide says "users can grant or revoke permissions at any time", so a read that worked yesterday can throw today. Google's read guide adds that if a user deletes your app, "all permissions, including the history permission, are revoked", so a reinstall starts from zero.
2. **Not declared in the manifest.** Google's get-started guide says the permissions in your request set must be "declared in your Android manifest first". Health Connect uses standard `uses-permission` tags, one per type and direction. A typo or a missing write entry fails here.
3. **A changes token that spans a revoked type.** Google's sync guide recommends "getting separate tokens per data type instead of getting them in bulk to avoid having an Exception in case one of the permissions is revoked." One token over five types fails as a whole when the user revokes any one of them.
4. **A background read without the background permission.** Android's [`HealthPermission` reference](https://developer.android.com/reference/kotlin/androidx/health/connect/client/permission/HealthPermission) says "An attempt to read data in background without this permission may result in an error" for `PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND`. It does not name the exception type, so confirm with your own logs whether this is what you are seeing.
5. **A published app without its Play Console declaration.** Google's table row tells you to declare your Health Connect data types for your published app. If a type works in a local build and fails only for Play users, see the production section of [Health Connect returns no data](/fix/health-connect-no-data).

## The fix: check the granted set, then call

Google's get-started guide is explicit that "your app needs to check for permissions every time before using them and handle scenarios where permission is lost." Ask `permissionController.getGrantedPermissions()` first, and keep a `catch` for the gap between your check and the call.

```kotlin
val stepsRead = HealthPermission.getReadPermission(StepsRecord::class)

suspend fun readStepsOrNull(
    client: HealthConnectClient,
    start: Instant,
    end: Instant
): List<StepsRecord>? {
    val granted = client.permissionController.getGrantedPermissions()
    if (stepsRead !in granted) return null // not granted: ask, don't call
    return try {
        client.readRecords(
            ReadRecordsRequest(
                StepsRecord::class,
                timeRangeFilter = TimeRangeFilter.between(start, end)
            )
        ).records
    } catch (e: SecurityException) {
        null // revoked between the check and the call
    }
}
```

A `null` here means "we may not look", which is different from an empty list. Keep the two apart in anything that draws charts or streaks.

When the permission is missing, request it with the Health Connect contract, which Google's get-started guide says displays the Health Connect permissions screen. Android's [`PermissionController` reference](https://developer.android.com/reference/kotlin/androidx/health/connect/client/PermissionController) documents `createRequestPermissionResultContract()`, and the callback returns the set the user actually granted:

```kotlin
val requestPermissions = registerForActivityResult(
    PermissionController.createRequestPermissionResultContract()
) { granted ->
    if (stepsRead in granted) {
        // safe to read
    } else {
        // denied: degrade, don't re-prompt in a loop
    }
}

requestPermissions.launch(setOf(stepsRead))
```

And the manifest side, in the format Google's get-started guide shows:

```xml
<uses-permission android:name="android.permission.health.READ_STEPS"/>
<uses-permission android:name="android.permission.health.WRITE_STEPS"/>
```

## Symptom to cause

| What you observe | Likely cause | What to do |
| --- | --- | --- |
| Throws on first use after install | Never granted | Check, then request through the contract |
| Worked, now throws on every call | User revoked it | Re-check every session; offer reconnect |
| `getChanges` throws after a settings change | One token covers a revoked type | One changes token per record type |
| Throws only in a background worker | Background permission missing | Request `PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND` |
| No exception, just an empty list | Not this error | Read [Health Connect returns no data](/fix/health-connect-no-data) |

## Where to go next

The full setup, including the manifest and permission flow this page assumes, is in the [Google Health Connect integration guide](/integrate/google-health-connect). Every `android.permission.health.*` string by record type is in the [Health Connect records reference](/health-connect/permissions). For an availability failure rather than a permission one, see [SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED](/fix/health-connect-not-available).

## Sources

- [developer.android.com/health-and-fitness/health-connect/read-data](https://developer.android.com/health-and-fitness/health-connect/read-data) — exception table: SecurityException description and best practice; permissions revoked on app deletion (checked 2026-10-03)
- [developer.android.com/health-and-fitness/health-connect/write-data](https://developer.android.com/health-and-fitness/health-connect/write-data) — same exception table on the write side (checked 2026-10-03)
- [developer.android.com/health-and-fitness/health-connect/get-started](https://developer.android.com/health-and-fitness/health-connect/get-started) — manifest declaration, getGrantedPermissions, createRequestPermissionResultContract, check permissions every time (checked 2026-10-03)
- [developer.android.com/health-and-fitness/health-connect/sync-data](https://developer.android.com/health-and-fitness/health-connect/sync-data) — separate changes tokens per data type to avoid an Exception when one permission is revoked (checked 2026-10-03)
- [developer.android.com/reference/kotlin/androidx/health/connect/client/HealthConnectClient](https://developer.android.com/reference/kotlin/androidx/health/connect/client/HealthConnectClient) — SecurityException under Throws for reads, writes, aggregate, getChanges; PERMISSION_WRITE_MEDICAL_DATA note (checked 2026-10-03)
- [developer.android.com/reference/kotlin/androidx/health/connect/client/PermissionController](https://developer.android.com/reference/kotlin/androidx/health/connect/client/PermissionController) — getGrantedPermissions and createRequestPermissionResultContract (checked 2026-10-03)
- [developer.android.com/reference/kotlin/androidx/health/connect/client/permission/HealthPermission](https://developer.android.com/reference/kotlin/androidx/health/connect/client/permission/HealthPermission) — PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND: reading in background without it may result in an error (checked 2026-10-03)
- [developer.android.com/reference/android/health/connect/HealthConnectException](https://developer.android.com/reference/android/health/connect/HealthConnectException) — ERROR_SECURITY description (checked 2026-10-03)
- [github.com/androidx/androidx/blob/androidx-main/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/ExceptionConverter.kt](https://github.com/androidx/androidx/blob/androidx-main/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/ExceptionConverter.kt) — Jetpack converts ERROR_SECURITY to SecurityException(this) (checked 2026-10-03)

## FAQ

### Is a Health Connect SecurityException the same as an empty result?

No. An empty list means the call was allowed and found nothing. A SecurityException means the call was not allowed at all. Google's exception table ties it to requests that need permissions that aren't granted. Keep the two apart in your code: an empty list can be shown as zero, but a SecurityException should lead to a permission check and a reconnect prompt, never a zero.

[Permalink](https://aifitnessapi.com/fix/health-connect-securityexception#faq-1)

### Why does getChanges throw SecurityException after the user changes permissions?

Most likely because one changes token covers several record types and the user revoked one of them. Google's sync guide recommends getting separate tokens per data type instead of getting them in bulk, to avoid an Exception if one of the permissions is revoked. With one token per type, losing one permission stops only that type's sync.

[Permalink](https://aifitnessapi.com/fix/health-connect-securityexception#faq-2)

### Is declaring a Health Connect permission in the manifest enough?

No. The manifest entry is what makes a permission requestable, and Google's get-started guide says every permission in your request set must be declared there first. Granting happens at runtime through the Health Connect permissions screen, which you open with PermissionController.createRequestPermissionResultContract. The user can also revoke a grant at any time, so check getGrantedPermissions before each use.

[Permalink](https://aifitnessapi.com/fix/health-connect-securityexception#faq-3)

### What is the HealthConnectException in my SecurityException stack trace?

On Android 14 and later the platform reports the failure as android.health.connect.HealthConnectException with the code ERROR_SECURITY, which Android describes as the caller lacking the correct permissions. The Jetpack library's source wraps that in a SecurityException, so the platform exception shows up as the cause. That wrapping is internal library code, so log the cause but do not branch on its message.

[Permalink](https://aifitnessapi.com/fix/health-connect-securityexception#faq-4)
