MuntashirAkon/AppManager · error · IOException

is a directory

Error message

 is a directory

What it means

openOutputStream(append) calls resolveFileOrNull(this.documentFile); when resolution yields null the underlying document is (or resolves to) a directory, and the method throws IOException(uri + " is a directory"). Directories have no byte stream, so opening an output stream on them is meaningless.

Source

Thrown at app/src/main/java/io/github/muntashirakon/io/PathImpl.java:1206

                    throw (FileNotFoundException) new FileNotFoundException("Could not open file " + file).initCause(e);
                }
            } // else use the default content provider
        } else if (documentFile instanceof VirtualDocumentFile) {
            int modeBits = ParcelFileDescriptor.parseMode(mode);
            try {
                return ((VirtualDocumentFile) documentFile).openFileDescriptor(modeBits);
            } catch (IOException e) {
                throw (FileNotFoundException) new FileNotFoundException(e.getMessage()).initCause(e);
            }
        }
        return FileUtils.getFdFromUri(context, documentFile.getUri(), mode);
    }

    @NonNull
    public OutputStream openOutputStream(boolean append) throws IOException {
        DocumentFile documentFile = resolveFileOrNull(this.documentFile);
        if (documentFile == null) {
            throw new IOException(this.documentFile.getUri() + " is a directory");
        }
        if (documentFile instanceof ExtendedRawDocumentFile) {
            try {
                return Objects.requireNonNull(getFile()).newOutputStream(append);
            } catch (IOException e) {
                throw new IOException("Could not open file for writing: " + documentFile.getUri(), e);
            }
        } else if (documentFile instanceof VirtualDocumentFile) {
            return ((VirtualDocumentFile) documentFile).openOutputStream(append);
        }
        String mode = "w" + (append ? "a" : "t");
        OutputStream os = context.getContentResolver().openOutputStream(documentFile.getUri(), mode);
        if (os == null) {
            throw new IOException("Could not resolve Uri: " + documentFile.getUri());
        }
        return os;
    }

View on GitHub (pinned to 0152f468fc)

Solutions

  1. Check path.isDirectory() before opening a stream
  2. Call getUri()/lastPathSegment checks or list files and open streams only on files
  3. Resolve to the intended child file inside the directory first

Example fix

// before
OutputStream os = path.openOutputStream();
// after
if (path.isDirectory()) throw new IllegalArgumentException("Expected a file: " + path.getUri());
OutputStream os = path.openOutputStream();
Defensive patterns

Strategy: validation

Validate before calling

if (path.isDirectory()) {
    throw new IllegalArgumentException("Cannot open output stream on a directory");
}

Try / catch

try {
    OutputStream os = path.openOutputStream(append);
} catch (IOException e) {
    if (e.getMessage() != null && e.getMessage().endsWith(" is a directory")) {
        // resolve to the intended child file and retry
    } else throw e;
}

Prevention

When it happens

Trigger: Calling path.openOutputStream()/openOutputStream(true) on a Path whose document is a directory (resolveFileOrNull returns null), e.g. a directory URI or a path that got resolved to a directory.

Common situations: Using a user-picked SAF tree/document URI that points at a folder, iterating entries without filtering out directories, or path construction that accidentally selects a directory.

Related errors


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