MuntashirAkon/AppManager · error · IOException

ERROR_MSG (constant; invalid/unknown file handle for calling

Error message

ERROR_MSG (constant; invalid/unknown file handle for calling pid)

What it means

FileContainer.get(handle) looks up an OpenFile by handle for the calling PID. It throws IOException("Requested file was not opened!") when there is no handle table at all for the calling process (files.get(pid) == null), meaning this process never opened any file through this container.

Source

Thrown at libcore/io/src/main/java/io/github/muntashirakon/io/FileContainer.java:26

import androidx.annotation.NonNull;

import java.io.IOException;

// 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;
    }

View on GitHub (pinned to 0152f468fc)

Solutions

  1. Ensure put() is called (via the FileSystemManager open API) in the same process before calling get().
  2. Re-open the file to obtain a fresh handle after a process restart or crash.
  3. Verify handles are not shared across processes; handles are per-PID and not transferable.

Example fix

// before
OpenFile f = container.get(handle); // throws if pid has no table
// after
try {
    OpenFile f = container.get(handle);
} catch (IOException e) {
    handle = container.put(reopenFile());
}
Defensive patterns

Strategy: try-catch

Validate before calling

boolean known = lastPutPid == android.os.Process.myPid() && lastPutHandle >= 0;

Try / catch

try {
    OpenFile f = container.get(handle);
} catch (IOException e) {
    if ("Requested file was not opened!".equals(e.getMessage())) {
        handle = container.put(openFresh());
    }
}

Prevention

When it happens

Trigger: Calling get(handle) from a process (Binder.getCallingPid()) that never registered any OpenFile via put() — e.g. a remote process requesting a handle it does not own, or calls made after the client process's table was cleared.

Common situations: IPC clients reusing a handle from a different process; the client process died and pidDied() purged its handles, then it retries; server restart losing in-memory handle tables.

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