MuntashirAkon/AppManager · error
IOException(ERROR_MSG)
Error message
IOException(ERROR_MSG)
What it means
Same lookup as error 644 but the failure is at the handle level: the calling PID has a handle table, but the requested handle number is not present (pidFiles.get(handle) == null). The handle was closed, never issued to this PID, or is stale.
Source
Thrown at libcore/io/src/main/java/io/github/muntashirakon/io/FileContainer.java:29
// Copyright 2022 John "topjohnwu" Wu
class FileContainer {
private final static String ERROR_MSG = "Requested file was not opened!";
private int nextHandle = 0;
// pid -> handle -> holder
private final SparseArray<SparseArray<OpenFile>> files = new SparseArray<>();
@NonNull
synchronized OpenFile get(int handle) throws IOException {
int pid = Binder.getCallingPid();
SparseArray<OpenFile> pidFiles = files.get(pid);
if (pidFiles == null)
throw new IOException(ERROR_MSG);
OpenFile h = pidFiles.get(handle);
if (h == null)
throw new IOException(ERROR_MSG);
return h;
}
synchronized int put(OpenFile h) {
int pid = Binder.getCallingPid();
SparseArray<OpenFile> pidFiles = files.get(pid);
if (pidFiles == null) {
pidFiles = new SparseArray<>();
files.put(pid, pidFiles);
}
int handle = nextHandle++;
pidFiles.append(handle, h);
return handle;
}
synchronized void remove(int handle) {
int pid = Binder.getCallingPid();
SparseArray<OpenFile> pidFiles = files.get(pid);View on GitHub (pinned to 0152f468fc)
Solutions
- Re-open the file to obtain a new handle whenever the old one throws this error.
- Track handle lifecycle: never use a handle after calling remove()/close().
- Guard remote calls so only handles returned by put() in the same process are passed.
Example fix
// before OpenFile f = container.get(oldHandle); // stale // after OpenFile f = container.get(currentHandle != -1 ? currentHandle : container.put(openNew()));
Defensive patterns
Strategy: try-catch
Validate before calling
Map<Integer, Boolean> liveHandles = new HashMap<>(); boolean valid = liveHandles.getOrDefault(handle, false);
Try / catch
try {
return container.get(handle);
} catch (IOException e) {
int fresh = container.put(reopen());
return container.get(fresh);
} Prevention
- Drop handles after close/remove
- Keep handle bookkeeping in one owner class
- Treat every handle as invalid after any IOException from get()
When it happens
Trigger: Calling get() with a handle that was already removed via remove(), a handle issued to a different PID, or an arbitrary/overflowed handle integer.
Common situations: Double-close then reuse of a handle; caching handles across binder calls after the file was closed; typos or misaligned index arithmetic when storing handles.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- ERROR_MSG (constant; invalid/unknown file handle for calling
- Invalid userId
- Can't unparcel type " + actual.getName() + " in list of type
- IOException(REMOTE_ERR_MSG, throwable)
- The IBinder provided is invalid
AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12).
Data as JSON: /api/errors/bd15258efb4023ff.
Report an issue: GitHub.