{"record":{"id":"1ae7c53013964e5c","repo":"MuntashirAkon/AppManager","slug":"selected-apk-splits-are-missing","errorCode":null,"errorMessage":"Selected APK splits are missing.","messagePattern":"Selected APK splits are missing\\.","errorType":"validation","errorClass":"JSONException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/ApkQueueItem.java","lineNumber":238,"sourceCode":"        mOriginatingUri = originatingUri != null ? Uri.parse(originatingUri) : null;\n        JSONObject apkSource = jsonObject.optJSONObject(\"apk_source\");\n        mApkSource = apkSource != null ? ApkSource.DESERIALIZER.deserialize(apkSource) : null;\n        JSONObject installerOptions = jsonObject.optJSONObject(\"installer_options\");\n        mInstallerOptions = installerOptions != null ? InstallerOptions.DESERIALIZER.deserialize(installerOptions) : null;\n        mSelectedSplits = JSONUtils.getArray(jsonObject.optJSONArray(\"selected_splits\"));\n    }\n\n    public void validateForReplay() throws JSONException {\n        if (mInstallExisting) {\n            if (TextUtils.isEmpty(mPackageName)) {\n                throw new JSONException(\"Package name is missing.\");\n            }\n        } else {\n            if (mApkSource == null) {\n                throw new JSONException(\"APK source is missing.\");\n            }\n            if (mSelectedSplits == null || mSelectedSplits.isEmpty()) {\n                throw new JSONException(\"Selected APK splits are missing.\");\n            }\n        }\n        if (mInstallerOptions != null) {\n            setInstallerOptions(InstallerOptions.resolveEffectiveOptions(mInstallerOptions,\n                    mPackageName, mTestOnly));\n        }\n    }\n\n    @NonNull\n    @Override\n    public JSONObject serializeToJson() throws JSONException {\n        JSONObject jsonObject = new JSONObject();\n        jsonObject.put(\"op_id\", mOperationId);\n        jsonObject.put(\"package_name\", mPackageName);\n        jsonObject.put(\"app_label\", mAppLabel);\n        jsonObject.put(\"install_existing\", mInstallExisting);\n        jsonObject.put(\"test_only\", mTestOnly);\n        jsonObject.put(\"originating_package\", mOriginatingPackage);","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/ApkQueueItem.java#L220-L256","documentation":"ApkQueueItem.validateForReplay() requires mSelectedSplits (the chosen split APKs of a split/APK-suite install) to be non-null and non-empty when mInstallExisting is false. A split APK cannot be installed without knowing which splits to include, so a JSONException is thrown.","triggerScenarios":"Replaying a queue item whose apkFile is a split APK suite but mSelectedSplits is null or empty — e.g. the selected_splits array was never saved in the JSON, or the user deselected all splits before queuing.","commonSituations":"Deserializing a saved install session missing the selected_splits key; programmatically building an ApkQueueItem for a split APK without calling the split-selection API; a UI flow that allowed confirming an install with zero splits checked.","solutions":["Persist/populate mSelectedSplits (selected_splits in JSON) with at least the base split before replay","For non-split packages, include the base entry id as the single selected split","Validate split selection in the UI (require at least the base split) before creating the queue item","Catch JSONException around getExecutableIntent and re-prompt the user to choose splits"],"exampleFix":"// before\nJSONObject obj = new JSONObject(json); // selected_splits absent\nApkQueueItem item = new ApkQueueItem(obj);\nitem.getExecutableIntent(context); // throws\n// after\nif (obj.optJSONArray(\"selected_splits\") == null) {\n    JSONArray splits = new JSONArray();\n    splits.put(\"base\");\n    obj.put(\"selected_splits\", splits);\n}\nApkQueueItem item = new ApkQueueItem(obj);\nitem.getExecutableIntent(context);","handlingStrategy":"validation","validationCode":"if (!item.isInstallExisting() && (item.getSelectedSplits() == null || item.getSelectedSplits().isEmpty())) {\n    throw new IllegalStateException(\"Select at least the base split before installing.\");\n}","typeGuard":"boolean hasSplits(ApkQueueItem item) {\n    return item.isInstallExisting()\n        || (item.getSelectedSplits() != null && !item.getSelectedSplits().isEmpty());\n}","tryCatchPattern":"try {\n    Intent i = item.getExecutableIntent(context);\n} catch (JSONException e) {\n    if (e.getMessage().contains(\"splits\")) {\n        openSplitSelectionScreen();\n    }\n}","preventionTips":["Require at least the base split to be selected in the UI before queuing","Persist selected_splits in session JSON","For non-split packages, default selected splits to the base entry id"],"tags":["android","apk-installer","split-apk","missing-argument"],"backgroundTag":"empty-required-field","analyzedSha":"0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5","analyzedAt":"2026-09-12T14:03:37.243Z","contentChangedAt":"2026-09-12T14:03:37.243Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}