asLody/VirtualApp · error · PackageManagerException

INSTALL_FAILED_INTERNAL_ERROR

INSTALL_FAILED_INTERNAL_ERROR

Error message

Session destroyed

What it means

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.

Solutions

  1. Do not cache PackageInstallerSession objects; obtain, fill, seal, and commit within a single lifecycle.
  2. Check session state (isDestroyed) before calling commit, or guard commit with try-catch for PackageManagerException.
  3. If a race is possible, serialize commit/abandon on one handler thread and never commit after abandon.
  4. Create a new session and re-stage the APK after this error; a destroyed session cannot be revived.

Example fix

// before
session.commit(statusReceiver); // session may already be destroyed
// after
if (!session.isDestroyed() && session.isSealed()) {
    session.commit(statusReceiver);
} else {
    int newId = installer.createSession(params);
    // re-open, stream APK, seal, commit on new session
}
Defensive patterns

Strategy: validation

Validate before calling

if (session.isDestroyed()) {
    throw new IllegalStateException("Cannot commit: session already destroyed");
}

Try / catch

try {
    session.commit(statusReceiver);
} catch (PackageManagerException e) {
    if ("Session destroyed".equals(e.getMessage())) {
        session = installer.createSession(new SessionParams(MODE_FULL_INSTALL));
    }
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of asLody/VirtualApp@666fefcb5d (2026-09-09). Data as JSON: /api/errors/a49513ad9a56ae08. Report an issue: GitHub.

Appendix: source

Thrown at VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/installer/PackageInstallerSession.java:154

            info.installerPackageName = installerPackageName;
            info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?
                    mResolvedBaseFile.getAbsolutePath() : null;
            info.progress = mProgress;
            info.sealed = mSealed;
            info.active = mActiveCount.get() > 0;

            info.mode = params.mode;
            info.sizeBytes = params.sizeBytes;
            info.appPackageName = params.appPackageName;
            info.appIcon = params.appIcon;
            info.appLabel = params.appLabel;
        }
        return info;
    }

    private void commitLocked() throws PackageManagerException {
        if (mDestroyed) {
            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
        }
        if (!mSealed) {
            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
        }
        try {
            resolveStageDir();
        } catch (IOException e) {
            e.printStackTrace();
        }
        validateInstallLocked();
        mInternalProgress = 0.5f;
        computeProgressLocked(true);
        // We've reached point of no return; call into PMS to install the stage.
        // Regardless of success or failure we always destroy session.
        final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
            @Override
            public void onUserActionRequired(Intent intent) {
                throw new IllegalStateException();

View on GitHub (pinned to 666fefcb5d)