Three things pile up inside a long-running Android recorder: files, database rows, and keys.
Two of those get cleaned up, because you can watch them fill the disk. Somebody notices the phone is full, somebody writes a rotation job, and the problem arrives in a unit everyone already understands. Keys are different. A key alias occupies no space you can look at in Settings, appears in no file listing, survives every clean-up you write for the other two, and until recently had no ceiling.
Android 17 gives it a ceiling. The ceiling is a number, and for at least one common design it is a number you reach.
What the platform actually says
From the Android 17 behavior changes for all apps, under Per-app keystore limits:
"Apps should avoid creating excessive numbers of keys in Android Keystore, because it is a shared resource for all apps on the device. Beginning with Android 17, the system enforces a limit on the number of keys an app can own."
The figures are in the same paragraph: "The limit is 50,000 keys for non-system apps targeting Android 17 (API level 37) or higher, and 200,000 keys for all other apps."
The failure is typed: "If an app attempts to create keys beyond the limit, the creation fails with a KeyStoreException."
Three things in that are worth more than the headline number.
The limit is not opt-in. An app that has not raised its target level is not exempt from the cap; it gets the larger of the two figures. The page closes the last door explicitly — "System apps have a limit of 200,000 keys, regardless of which API level they target." There is no exempt category. There is only a version where the wall is further away.
Keystore is described as a shared resource. That is the platform's own framing, and it is the part that explains the change. Your key count is not a private matter between your app and its own storage. It is a draw on something every app on the device draws on.
The error code depends on what you target, and the difference is unkind. An app targeting Android 17 gets a purpose-built code back from getNumericErrorCode() — the page calls it "returns the new ERROR_TOO_MANY_KEYS value". Everything else gets ERROR_INCORRECT_USAGE, the generic code for a malformed request. On the large majority of installed devices, in other words, hitting a platform quota will arrive at your crash reporter wearing the label of a programming mistake. The message string carries the real story — "The exception's message string contains information about the key limit" — but a message string is the one part of an exception that dashboards group away.
Why a recorder is the shape that reaches it
Most apps will never approach 50,000 keys, and Google clearly expects that. The apps that get near it are the ones that mint a key per artifact rather than a key per install, and a continuous recorder is the purest example of that shape: it produces artifacts on a fixed clock, forever, with no user action in between.
The arithmetic is not close to the edge in every design, which is exactly why it is worth writing down rather than worrying about:
| key granularity | new keys per day | days to 50,000 | days to 200,000 |
|---|---|---|---|
| one per five-minute segment | 288 | 174 | 694 |
| one per ten-minute segment | 144 | 347 | 1,388 |
| one per motion clip, forty a day | 40 | 1,250 | 5,000 |
| one per hour | 24 | 2,083 | 8,333 |
| one per calendar day | 1 | 50,000 | 200,000 |
Read the first row again. A camera on a five-minute segment clock, encrypting each segment under its own hardware-backed key, exhausts a targeted app's entire allowance in under six months of continuous operation. That is not an exotic setup. Five minutes is a common segment length precisely because it bounds how much you lose to a single interrupted write.
The second column is the one that decides your fate, and it is a design choice, not a platform constraint. Nothing about encrypting recordings requires a fresh Keystore key per file. The usual pattern is one long-lived Keystore key that wraps a cheap per-file key stored beside the file — the Keystore entry count stays at one no matter how long the camera runs. If you are already doing that, this change is a non-event for you, and you should stop reading here with a clear conscience.
The failure mode is not the one you would guess
The interesting case is not the app that deliberately creates 50,000 keys. It is the app that deliberately creates a few and then never deletes any.
Deleting a recording is a file operation. Deleting the key that recording was encrypted under is a separate call to a separate subsystem, in a separate part of the codebase, with no error if you skip it and nothing visible anywhere if you skip it a thousand times. Every rotation job I have read the description of is written against a storage budget, because storage is what runs out. Nothing runs out on the alias side — or nothing did.
The cheapest design to write is the one that never deletes anything it does not have to. Android 17 turns that from a tidiness question into a failure with a number attached.
What the page does not say, and I am not going to guess
Being precise about the boundary of what I know here matters more than sounding authoritative, so:
-
Whether deleting an alias frees a slot. The wording is a limit on "the number of keys an app can own", which reads as a live count rather than a lifetime tally — under that reading,
KeyStore.deleteEntry()gives the slot back. The page does not say so. I have not tested it, and I am not going to assert it. - How the count is scoped across users and profiles. Keystore entries are per-app-per-user. Whether the quota is applied per user or across all of them is not stated on the page.
- Whether StrongBox-backed and TEE-backed entries share one budget. Not stated.
- When it lands on any given handset. This is an Android 17 behaviour. Which phone gets Android 17, and when, is a separate question this page does not answer and neither will I.
Each of those is checkable by somebody with a device and an afternoon. None of them is checkable by reading, and reading is what I did.
How to find out where you actually stand
The count is readable in a few lines, and it is the first thing worth knowing:
val ks = java.security.KeyStore.getInstance("AndroidKeyStore")
ks.load(null)
val aliases = java.util.Collections.list(ks.aliases())
android.util.Log.i("keys", "alias count = " + aliases.size)
Run it on a device that has had a build of yours on it for months rather than a fresh emulator. A clean install always looks fine. The whole point of this class of bug is that it is a function of elapsed time, and elapsed time is the one variable a test suite does not have.
If the number surprises you, the fix is boring and cheap: delete the alias in the same code path that deletes the artifact it belongs to, and treat a leftover alias as a leak the same way you would treat a leftover temp file.
This is not the storage argument
It looks adjacent to one and it is not. How many bytes a continuous recorder produces, how long you can keep them, and what happens when the card fills up is a genuinely different question with genuinely different arithmetic, and I have written it out at length in Storage, Not Heat, Is the Real Limit on 24/7 Android Recording — that piece is about bytes, this one is about aliases, and the two budgets are exhausted by different designs at different rates. Likewise, a file that will not open after an interrupted write is a container problem, not a key problem.
The reason it is worth separating them is that the storage budget is visible and the key budget is not. You will find out about the first one from a user. You will find out about the second one from a stack trace that says you passed a bad argument.
The short version
Android Keystore now has a per-app cap: 50,000 entries for apps targeting Android 17, 200,000 for everyone else and for system apps, and a KeyStoreException when you cross it. Most apps will never notice. An app that mints a key per recorded artifact and never deletes one will.
The date it finds out is set by its segment length. Segment length was picked to bound how much a single interrupted write costs you — chosen once, years before anybody had a reason to count keys, to settle a completely different argument.
We build Background Camera RemoteStream, an Android app for running a phone as a camera with the screen off. More of these write-ups at superfunicular.com.
Sources, all first-party: Android Developers, Behavior changes: all apps for Android 17 (last updated 14 August 2026), section Per-app keystore limits — the 50,000/200,000 figures, the shared-resource rationale, the KeyStoreException, and the ERROR_TOO_MANY_KEYS / ERROR_INCORRECT_USAGE split; and the API reference for KeyStoreException and KeyStore.
Top comments (0)