MuntashirAkon/AppManager · error · FileNotFoundException

Couldn't find any writable Obb dir

Error message

Couldn't find any writable Obb dir

What it means

getWritableExternalDirectory iterates candidate external storage directories and throws FileNotFoundException when none is writable (or on /storage/emulated). It is used by obbDir() to locate a directory where OBB files can be written; without a writable external dir, OBB installation cannot proceed.

Source

Thrown at app/src/main/java/io/github/muntashirakon/AppManager/apk/ApkUtils.java:312

                .findOrCreateDirectory("obb")
                .findOrCreateDirectory(packageName);
        return Paths.get(obbDir.getUri());
    }

    @NonNull
    public static Path getWritableExternalDirectory(@UserIdInt int userId) throws FileNotFoundException {
        // Get the first writable external storage directory
        OsEnvironment.UserEnvironment userEnvironment = OsEnvironment.getUserEnvironment(userId);
        Path[] extDirs = userEnvironment.getExternalDirs();
        Path writableExtDir = null;
        for (Path extDir : extDirs) {
            if (extDir.canWrite() || Objects.requireNonNull(extDir.getFilePath()).startsWith("/storage/emulated")) {
                writableExtDir = extDir;
                break;
            }
        }
        if (writableExtDir == null) {
            throw new FileNotFoundException("Couldn't find any writable Obb dir");
        }
        return writableExtDir;
    }

    public static int getDensityFromName(@Nullable String densityName) {
        Integer density = StaticDataset.DENSITY_NAME_TO_DENSITY.get(densityName);
        if (density == null) {
            throw new IllegalArgumentException("Unknown density " + densityName);
        }
        return density;
    }

    @NonNull
    private static ByteBuffer getAndroidManifestFromApk(
            @NonNull List<CentralDirectoryRecord> cdRecords, @NonNull DataSource lhfSection)
            throws IOException, ApkFormatException, ZipFormatException {
        CentralDirectoryRecord androidManifestCdRecord = findCdRecord(cdRecords, MANIFEST_FILE);
        if (androidManifestCdRecord == null) {

View on GitHub (pinned to 0152f468fc)

Solutions

  1. Grant storage permissions (MANAGE_EXTERNAL_STORAGE on Android 11+, or READ/WRITE_EXTERNAL_STORAGE with legacy storage) before calling obbDir().
  2. Check that at least one external storage volume is mounted and writable via Environment.getExternalStorageState().
  3. Fall back to app-private storage (context.getExternalFilesDirs or getObbDirs) and handle the exception by copying OBB data there.
  4. Wrap obbDir() in try-catch for FileNotFoundException and surface a user-facing 'no writable storage' message.

Example fix

// before
File obbDir = ApkUtils.obbDir();
// after
File obbDir;
try {
    obbDir = ApkUtils.obbDir();
} catch (FileNotFoundException e) {
    obbDir = context.getExternalFilesDir(null); // fallback
}
Defensive patterns

Strategy: fallback

Validate before calling

boolean hasWritable = Arrays.stream(context.getExternalFilesDirs(null))
        .anyMatch(d -> d != null && d.canWrite());
if (!hasWritable) requestStoragePermissionsOrAbort();

Try / catch

try {
    File obbDir = ApkUtils.obbDir();
} catch (FileNotFoundException e) {
    File fallback = context.getExternalFilesDir(null);
}

Prevention

When it happens

Trigger: Calling ApkUtils.obbDir() when every enumerated external directory is read-only or not under /storage/emulated — e.g. fully adopted storage, restricted storage permissions, or scoped-storage restrictions on Android 11+ where no writable external dir is found.

Common situations: Running on devices with no SD card and denied MANAGE_EXTERNAL_STORAGE/READ_WRITE permissions, Android 11+ scoped storage restricting OBB paths, emulators with unusual storage layouts, or work-profile/restricted users.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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