{"record":{"id":"e9a302cd5298cd05","repo":"MuntashirAkon/AppManager","slug":"apk-source-is-missing","errorCode":null,"errorMessage":"APK source is missing.","messagePattern":"APK source is missing\\.","errorType":"validation","errorClass":"JSONException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/ApkQueueItem.java","lineNumber":235,"sourceCode":"        mTestOnly = jsonObject.optBoolean(\"test_only\", false);\n        mOriginatingPackage = JSONUtils.optString(jsonObject, \"originating_package\", null);\n        String originatingUri = JSONUtils.optString(jsonObject, \"originating_uri\", null);\n        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);","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/ApkQueueItem.java#L217-L253","documentation":"ApkQueueItem.validateForReplay() validates a queue item before replaying an installation. When the item is NOT an install-existing replay (mInstallExisting == false), mApkSource must point at the APK file(s) to install; if it is null the item has no APK to install and a JSONException is thrown.","triggerScenarios":"Replaying an install queue item where mInstallExisting is false but mApkSource was never set — e.g. the item was created/deserialized from JSON without the apk_source field, or the source URI/file was dropped during persistence.","commonSituations":"Restoring a queued install from a saved JSON session where the apk_source key is absent; constructing an ApkQueueItem programmatically and forgetting to set the APK source; a cleared/revoked content-URI permission causing the source to be discarded.","solutions":["Ensure mApkSource (apk_source in the serialized JSON) is set before replaying the queue item","If the intent is to reinstall an already-installed package, set mInstallExisting=true and provide mPackageName instead of an APK source","Check that the original content/file URI permission still exists so the source is not lost between sessions","Wrap getExecutableIntent/validateForReplay calls in try-catch for JSONException and surface a user-facing 'select an APK' prompt"],"exampleFix":"// before\nJSONObject obj = new JSONObject(json);\nApkQueueItem item = new ApkQueueItem(obj); // apk_source missing\nitem.getExecutableIntent(context); // throws\n// after\nif (obj.optString(\"apk_source\", null) == null && !obj.optBoolean(\"install_existing\")) {\n    obj.put(\"apk_source\", pickedApkUri.toString());\n}\nApkQueueItem item = new ApkQueueItem(obj);\nitem.getExecutableIntent(context);","handlingStrategy":"validation","validationCode":"if (item.getApkSource() == null) { promptApkSelection(); }","typeGuard":"boolean hasSource(ApkQueueItem i) { return i.getApkSource() != null; }","tryCatchPattern":"try { item.getExecutableIntent(ctx); } catch (JSONException e) { log(e); }","preventionTips":[],"tags":["android","apk-installer","missing-argument","json"],"backgroundTag":"missing-required-argument","analyzedSha":"0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5","analyzedAt":"2026-09-12T14:03:37.243Z","contentChangedAt":"2026-09-12T14:03:37.243Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}