{"record":{"id":"a49513ad9a56ae08","repo":"asLody/VirtualApp","slug":"install-failed-internal-error","errorCode":"INSTALL_FAILED_INTERNAL_ERROR","errorMessage":"Session destroyed","messagePattern":"Session destroyed","errorType":"error_code","errorClass":"PackageManagerException","httpStatus":null,"severity":"error","filePath":"VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/installer/PackageInstallerSession.java","lineNumber":154,"sourceCode":"            info.installerPackageName = installerPackageName;\n            info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?\n                    mResolvedBaseFile.getAbsolutePath() : null;\n            info.progress = mProgress;\n            info.sealed = mSealed;\n            info.active = mActiveCount.get() > 0;\n\n            info.mode = params.mode;\n            info.sizeBytes = params.sizeBytes;\n            info.appPackageName = params.appPackageName;\n            info.appIcon = params.appIcon;\n            info.appLabel = params.appLabel;\n        }\n        return info;\n    }\n\n    private void commitLocked() throws PackageManagerException {\n        if (mDestroyed) {\n            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, \"Session destroyed\");\n        }\n        if (!mSealed) {\n            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, \"Session not sealed\");\n        }\n        try {\n            resolveStageDir();\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n        validateInstallLocked();\n        mInternalProgress = 0.5f;\n        computeProgressLocked(true);\n        // We've reached point of no return; call into PMS to install the stage.\n        // Regardless of success or failure we always destroy session.\n        final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {\n            @Override\n            public void onUserActionRequired(Intent intent) {\n                throw new IllegalStateException();","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/asLody/VirtualApp/blob/666fefcb5d3f39cc944001c3457c38ffd6544c87/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/installer/PackageInstallerSession.java#L136-L172","documentation":"commitLocked checks mDestroyed before committing an install session. If the session was already destroyed (abandoned or timed out), it throws PackageManagerException with code INSTALL_FAILED_INTERNAL_ERROR and message \"Session destroyed\". A destroyed session can no longer be committed; its staging state is gone.","triggerScenarios":"Calling PackageInstallerSession.commit() after destroy()/abandon() was called, or after the handler already processed a destroy message; committing a session object held past its lifecycle (e.g. cached and reused later).","commonSituations":"App keeps a long-lived reference to the session and commits much later; user cancels install (abandon) while a background thread still calls commit; double-commit race handled via the handler queue.","solutions":["Do not cache PackageInstallerSession objects; obtain, fill, seal, and commit within a single lifecycle.","Check session state (isDestroyed) before calling commit, or guard commit with try-catch for PackageManagerException.","If a race is possible, serialize commit/abandon on one handler thread and never commit after abandon.","Create a new session and re-stage the APK after this error; a destroyed session cannot be revived."],"exampleFix":"// before\nsession.commit(statusReceiver); // session may already be destroyed\n// after\nif (!session.isDestroyed() && session.isSealed()) {\n    session.commit(statusReceiver);\n} else {\n    int newId = installer.createSession(params);\n    // re-open, stream APK, seal, commit on new session\n}","handlingStrategy":"validation","validationCode":"if (session.isDestroyed()) {\n    throw new IllegalStateException(\"Cannot commit: session already destroyed\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    session.commit(statusReceiver);\n} catch (PackageManagerException e) {\n    if (\"Session destroyed\".equals(e.getMessage())) {\n        session = installer.createSession(new SessionParams(MODE_FULL_INSTALL));\n    }\n}","preventionTips":["Never cache sessions across user interactions; use them immediately.","Pair every abandon() with dropping the session reference.","Serialize all session lifecycle calls on one thread/handler."],"tags":["android","package-install","session-lifecycle","race-condition"],"backgroundTag":"invalid-state-transition","analyzedSha":"666fefcb5d3f39cc944001c3457c38ffd6544c87","analyzedAt":"2026-09-09T11:09:01.694Z","contentChangedAt":"2026-09-09T11:09:01.694Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}