asLody/VirtualApp · error · IllegalStateException

before prepared

Error message

 before prepared

What it means

PackageInstallerSession tracks session lifecycle with an mPrepared flag, set only when open() is called with a valid stageDir/stageCid. assertPreparedAndNotSealed guards getNames, openWrite, and openRead; if any of these is invoked before the session was prepared, it throws IllegalStateException('<cookie> before prepared'). The library enforces that a staging session must be opened/initialized before any file operations.

Solutions

  1. Call session.open(stageDir, currentUser.id) (or the service API that opens the session) before any openWrite/openRead/getNames call.
  2. Use PackageInstaller.openSession(sessionId) to get a properly prepared session rather than constructing/holding the raw session object.
  3. If managing sessions directly, verify session.mPrepared (or track your own 'opened' state) before file operations.

Example fix

// before
PackageInstallerSession session = createSession(params);
OutputStream out = session.openWrite("base.apk", 0, -1); // IllegalStateException

// after
PackageInstallerSession session = createSession(params);
session.open(new File(VEnvironment.getPackageInstallerStageDir(), String.valueOf(session.sessionId)), userId);
OutputStream out = session.openWrite("base.apk", 0, -1);
Defensive patterns

Strategy: validation

Validate before calling

// Java: track session state yourself
if (!sessionOpened) {
    throw new IllegalStateException("Call open(stageDir, userId) before openWrite/openRead/getNames");
}

Type guard

// Java: wrap session and expose isOpened
boolean isReady(PackageInstallerSession s) { return s != null && s.mPrepared; }

Try / catch

try {
    stream = session.openWrite(name, offset, length);
} catch (IllegalStateException e) {
    if (e.getMessage() != null && e.getMessage().endsWith("before prepared")) {
        session.open(stageDir, userId);
        stream = session.openWrite(name, offset, length);
    } else throw e;
}

Prevention

When it happens

Trigger: Calling session.getNames(), session.openWrite(name,...) or session.openRead(name) on a PackageInstallerSession that was created but never had open(stageDir, userId) invoked, so mPrepared is still false.

Common situations: Obtaining a session object through an unusual path (e.g. reconstructed or looked-up session) and skipping the open() step; calling openWrite immediately after createSession without opening; race where another thread checks before the open() synchronized block completes.

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/9e4e3069d1b88a01. Report an issue: GitHub.

Appendix: source

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

                }
            }
            return mResolvedStageDir;
        }
    }

    @Override
    public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) throws RemoteException {
        try {
            return openWriteInternal(name, offsetBytes, lengthBytes);
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }

    private void assertPreparedAndNotSealed(String cookie) {
        synchronized (mLock) {
            if (!mPrepared) {
                throw new IllegalStateException(cookie + " before prepared");
            }
            if (mSealed) {
                throw new SecurityException(cookie + " not allowed after commit");
            }
        }
    }


    private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes)
            throws IOException {
        // Quick sanity check of state, and allocate a pipe for ourselves. We
        // then do heavy disk allocation outside the lock, but this open pipe
        // will block any attempted install transitions.
        final FileBridge bridge;
        synchronized (mLock) {
            assertPreparedAndNotSealed("openWrite");

            bridge = new FileBridge();

View on GitHub (pinned to 666fefcb5d)