fdroid update crashes on duplicate permission
If an app declares a permission twice, and one of the declarations has maxSdkVersion set while the other has not, fdroid update crashes with a stack trace, requiring to check the code to find out what went wrong (and giving no clue which APK file caused the crash). Example (APK attached: sadLogic.HomeCentral_8.apk):
uses-permission: name='android.permission.WRITE_EXTERNAL_STORAGE' maxSdkVersion='18'
uses-permission: name='android.permission.WRITE_EXTERNAL_STORAGE'
Trace:
2025-05-14 21:10:14,666 CRITICAL: Unknown exception found!
Traceback (most recent call last):
File "/mnt/data/src/git/fdroidserver/fdroid", line 22, in <module>
fdroidserver.__main__.main()
File "/home/git/repos/fdroidserver_iod/fdroidserver/__main__.py", line 222, in main
raise e
File "/home/git/repos/fdroidserver_iod/fdroidserver/__main__.py", line 203, in main
mod.main()
File "/home/git/repos/fdroidserver_iod/fdroidserver/update.py", line 2594, in main
fdroidserver.index.make(repoapps, apks, repodirs[0], False)
File "/home/git/repos/fdroidserver_iod/fdroidserver/index.py", line 121, in make
make_v0(sortedapps, apks, repodir, repodict, requestsdict,
File "/home/git/repos/fdroidserver_iod/fdroidserver/index.py", line 1274, in make_v0
sorted_permissions = sorted(apk['uses-permission'])
TypeError: '<' not supported between instances of 'NoneType' and 'int'
While from this stack trace, one might be able to deduce what happened – it remains completely unclear which APK file has this issue. With hundreds or thousands of apps in the repository (like at F-Droid.org or IzzyOnDroid), this can be "fun" to figure. So as a quick work-around, giving a more meaningful message (and especially mentioning the culprit), we've applied this for now:
diff --git a/fdroidserver/index.py b/fdroidserver/index.py
index 69237149..4434079e 100644
--- a/fdroidserver/index.py
+++ b/fdroidserver/index.py
@@ -1257,7 +1271,10 @@ def make_v0(apps, apks, repodir, repodict, requestsdict, fdroid_signing_key_fing
addElement('sig', apk['sig'], doc, apkel)
old_permissions = set()
- sorted_permissions = sorted(apk['uses-permission'])
+ try:
+ sorted_permissions = sorted(apk['uses-permission'])
+ except Exception:
+ raise FDroidException("Failed to sort permissions for {0}: {1}".format(apk['apkName'], ', '.join(map(str,apk['uses-permission']))))
for perm in sorted_permissions:
perm_name = perm[0]
if perm_name.startswith("android.permission."):
So at least "offender" and "cause" become immediately clear, and the offending APK can be moved out as a "quick remedy" to get things working again:
2025-05-15 10:34:38,494 CRITICAL: Failed to sort permissions for sadLogic.HomeCentral_8.apk: ['android.permission.WRITE_EXTERNAL_STORAGE', 18], ['android.permission.WRITE_EXTERNAL_STORAGE', None], ['android.permission.WAKE_LOCK', None], ['android.permission.INTERNET', None], ['android.permission.RECEIVE_BOOT_COMPLETED', None], ['android.permission.FOREGROUND_SERVICE', None], ['android.permission.WRITE_SETTINGS', None], ['android.permission.ACCESS_NOTIFICATION_POLICY', None], ['android.permission.READ_EXTERNAL_STORAGE', None], ['android.permission.REQUEST_INSTALL_PACKAGES', None]
I'm intentionally not providing this via an MR as I feel the proper handling should be throwing that message, but then continuing while ignoring the affected APK, so automated runs are not broken. Such occurrences should be rather rare, though – but can happen (and not just in binary repos). Up to you to use the patch as-is, or change the handling to ignore the APK as proposed.