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

  1. Re-open the file to obtain a new handle whenever the old one throws this error.
  2. Track handle lifecycle: never use a handle after calling remove()/close().
  3. 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

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


AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12). Data as JSON: /api/errors/bd15258efb4023ff. Report an issue: GitHub.