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
- Call session.open(stageDir, currentUser.id) (or the service API that opens the session) before any openWrite/openRead/getNames call.
- Use PackageInstaller.openSession(sessionId) to get a properly prepared session rather than constructing/holding the raw session object.
- 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
- Always open the session immediately after createSession.
- Centralize session creation + open in one factory method.
- Avoid caching raw session objects across calls.
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
- not allowed after commit
- Invalid name:
- Must specify package name to remove a split
- Files still open
- Exactly one of stageDir or stageCid stage must be set
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)