MuntashirAkon/AppManager · error · KeyStoreException
Decrypted pass is empty for alias
Error message
Decrypted pass is empty for alias ${alias} What it means
getAliasPassword successfully read the stored encrypted password string for the alias, but getDecryptedPassword returned null, so it throws KeyStoreException "Decrypted pass is empty for alias ...". The alias's password blob exists but cannot be decrypted with the current crypto setup.
Solutions
- Re-encrypt and re-save the alias password (delete the pref entry and prompt the user to re-enter it).
- Verify the Android Keystore key used for decryption still exists and is usable; regenerate if invalidated.
- If data was restored from a backup, re-import the keystore entry with the alias password instead of relying on the restored blob.
Example fix
// before
password = getDecryptedPassword(mContext, encryptedPass);
if (password == null) {
throw new KeyStoreException("Decrypted pass is empty for alias " + alias);
}
// after
password = getDecryptedPassword(mContext, encryptedPass);
if (password == null) {
sSharedPreferences.edit().remove(prefAlias).apply();
password = promptForAliasPassword(alias); // re-encrypt and store, then return
if (password == null) throw new KeyStoreException("Decrypted pass is empty for alias " + alias);
} Defensive patterns
Strategy: try-catch
Validate before calling
// ensure the decryption key is usable before attempting alias password reads
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
if (!ks.containsAlias(cryptoAlias)) {
regenerateCryptoKey(); // decryption key was wiped/invalidated
} Try / catch
// try
try {
char[] pass = keyStoreManager.getKey(alias);
} catch (KeyStoreException e) {
if (e.getMessage().contains("Decrypted pass is empty")) {
// blob present but undecryptable: re-key and re-store the password
reEncryptAliasPassword(alias);
}
} Prevention
- Detect Android Keystore key invalidation after lock-screen/biometric changes and re-encrypt stored secrets.
- Don't restore encrypted prefs from another device's backup.
- Wrap getDecryptedPassword results with a non-null check and a recovery path in library usage.
- Version the encrypted blob format so migrations can run before decryption.
When it happens
Trigger: Calling getAliasPassword(alias) (via getKey) where the pref exists and is a valid String, but getDecryptedPassword(mContext, encryptedPass) yields null — Android Keystore key invalidated, wrong user authentication state, or corrupted ciphertext.
Common situations: Biometric/lock-screen change invalidated the decryption key; app restored from another device so ciphertext and key don't match; encrypted blob truncated or corrupted.
Related errors
- Could not decrypt encrypted password.
- Could not load AES local protection key from keystore
- Failed to setup metadata.
- No KeyPair with alias
- No KeyPair with alias
AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12).
Data as JSON: /api/errors/46c9d956add5747c.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/main/java/io/github/muntashirakon/AppManager/crypto/ks/KeyStoreManager.java:503
/**
* @return Password for the given alias. {@link Utils#clearChars(char[])} must be called when done.
* @deprecated Kept for migratory purposes only, deprecated since v2.6.3. To be removed in v3.0.0.
*/
@Deprecated
@CheckResult
@NonNull
private char[] getAliasPassword(@NonNull String alias) throws KeyStoreException {
char[] password;
String prefAlias = getPrefAlias(alias);
if (sSharedPreferences.contains(prefAlias)) {
String encryptedPass = sSharedPreferences.getString(prefAlias, null);
if (encryptedPass == null) {
throw new KeyStoreException("Stored pass is empty for alias " + alias);
}
password = getDecryptedPassword(mContext, encryptedPass);
if (password == null) {
throw new KeyStoreException("Decrypted pass is empty for alias " + alias);
}
return password;
} else {
IntentFilter filter = new IntentFilter(ACTION_KS_INTERACTION_BEGIN);
filter.addAction(ACTION_KS_INTERACTION_END);
ContextCompat.registerReceiver(mContext, mReceiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED);
Intent broadcastIntent = new Intent(ACTION_KS_INTERACTION_BEGIN);
broadcastIntent.setPackage(mContext.getPackageName());
mContext.sendBroadcast(broadcastIntent);
// Intent wrapper
Intent intent = new Intent(mContext, KeyStoreActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(KeyStoreActivity.EXTRA_ALIAS, alias);
String ks = "AM KeyStore";
// We don't need a delete intent since the time will be expired anyway
NotificationCompat.Builder builder = NotificationUtils.getHighPriorityNotificationBuilder(mContext)
.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_ALL)View on GitHub (pinned to 0152f468fc)