asLody/VirtualApp · error · java.lang.SecurityException
Must be sealed to accept permissions
Error message
Must be sealed to accept permissions
What it means
When an install requires runtime-permission confirmation, the system may ask the user; setPermissionsResult(accepted) records the answer and kicks off the next install pass, but only if the session is already sealed (committed). Calling it before commit() throws SecurityException('Must be sealed to accept permissions').
Solutions
- Ensure commit() was called and completed (mSealed true) before relaying the permission decision.
- Verify the sessionId in the permission callback matches the committed session; ignore stale callbacks.
- If the session was abandoned, drop the permission response instead of calling setPermissionsResult.
Example fix
// before
if (promptAccepted) session.setPermissionsResult(true); // SecurityException if unsealed
// after
if (promptAccepted && session.sealed) {
session.setPermissionsResult(true);
} else {
Log.w(TAG, "Ignoring permission result for unsealed/unknown session");
} Defensive patterns
Strategy: try-catch
Validate before calling
// Only forward results for sessions you committed yourself if (!isSessionCommitted(sessionId)) return; // drop stale permission callback
Try / catch
try {
session.setPermissionsResult(accepted);
} catch (SecurityException e) {
if (String.valueOf(e.getMessage()).contains("Must be sealed")) {
Log.w(TAG, "Permission result for unsealed session ignored");
} else throw e;
} Prevention
- Only relay permission results through the flow initiated by commit().
- Match callback sessionId against your tracked committed sessions.
- Ignore/discard results for abandoned sessions.
When it happens
Trigger: Delivering the permission-accept/deny callback for a session that has not gone through commit(); invoking setPermissionsResult manually out of order or twice across unsealed sessions.
Common situations: Custom installer UI that responds to a permission prompt for a session which was concurrently abandoned or never committed; race between commit() and the user's permission answer; wrong sessionId passed to the callback.
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
- INSTALL_FAILED_INTERNAL_ERROR
- not allowed after commit
- Files still open
- Caller has no access to session
- Unable to create application
AI-assisted analysis of asLody/VirtualApp@666fefcb5d (2026-09-09).
Data as JSON: /api/errors/2577a38a39900989.
Report an issue: GitHub.
Appendix: source
Thrown at VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/installer/PackageInstallerSession.java:458
private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
mFinalStatus = returnCode;
mFinalMessage = msg;
if (mRemoteObserver != null) {
try {
mRemoteObserver.onPackageInstalled(mPackageName, returnCode, msg, extras);
} catch (RemoteException ignored) {
}
}
final boolean success = (returnCode == INSTALL_SUCCEEDED);
mCallback.onSessionFinished(this, success);
}
void setPermissionsResult(boolean accepted) {
if (!mSealed) {
throw new SecurityException("Must be sealed to accept permissions");
}
if (accepted) {
// Mark and kick off another install pass
synchronized (mLock) {
mPermissionsAccepted = true;
}
mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
} else {
destroyInternal();
dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
}
}
public void open() throws IOException {
if (mActiveCount.getAndIncrement() == 0) {
mCallback.onSessionActiveChanged(this, true);
}View on GitHub (pinned to 666fefcb5d)