MuntashirAkon/AppManager · error · IOException

Unable to open output stream.

Error message

Unable to open output stream.

What it means

When saving an app icon, the fragment opens an OutputStream to the target Uri via Paths.get(uri).openOutputStream(). Android's content resolvers can return null instead of throwing when the provider cannot supply a stream (bad Uri, provider unavailable, no write permission). Because the try-with-resources would otherwise NPE later, the code explicitly throws IOException('Unable to open output stream.') to surface the failure.

Source

Thrown at app/src/main/java/io/github/muntashirakon/AppManager/details/info/AppInfoFragment.java:610

                        int flags = 0;
                        for (int flag : selections) {
                            flags |= flag;
                        }
                        NetworkPolicyManagerCompat.setUidPolicy(mApplicationInfo.uid, flags);
                        mMainModel.getTagsAlteredLiveData().setValue(true);
                    })
                    .show();
        } else if (itemId == R.id.action_extract_icon) {
            String iconName = mAppLabel + "_icon.png";
            mExport.launch(iconName, uri -> {
                if (uri == null) {
                    // Back button pressed.
                    return;
                }
                ThreadUtils.postOnBackgroundThread(() -> {
                    try (OutputStream outputStream = Paths.get(uri).openOutputStream()) {
                        if (outputStream == null) {
                            throw new IOException("Unable to open output stream.");
                        }
                        Bitmap bitmap = getBitmapFromDrawable(mApplicationInfo.loadIcon(mPackageManager));
                        bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
                        outputStream.flush();
                        ThreadUtils.postOnMainThread(() -> displayShortToast(R.string.saved_successfully));
                    } catch (IOException e) {
                        Log.e(TAG, e);
                        ThreadUtils.postOnMainThread(() -> displayShortToast(R.string.saving_failed));
                    }
                });
            });
        } else if (itemId == R.id.action_install) {
            List<UserInfo> users = Users.getUsers();
            CharSequence[] userNames = new String[users.size()];
            int i = 0;
            for (UserInfo info : users) {
                userNames[i++] = info.toLocalizedString(requireContext());
            }

View on GitHub (pinned to 0152f468fc)

Solutions

  1. Re-pick the destination with the Storage Access Framework and retry the save
  2. Check persisted URI permissions (context.checkUriPermission / persistedUriPermissions) and re-request if lost
  3. Verify the Uri scheme is supported and the target document still exists before saving
  4. Fall back to saving via MediaStore or app-external storage if the provider stream fails

Example fix

// before
OutputStream os = Paths.get(uri).openOutputStream(); // may return null -> IOException
// after
OutputStream os = Paths.get(uri).openOutputStream();
if (os == null) {
    // re-pick destination or fall back
    requestNewDestinationUri();
    return;
}
Defensive patterns

Strategy: try-catch

Validate before calling

OutputStream os = Paths.get(uri).openOutputStream();
boolean writable = os != null;
if (os != null) os.close();

Type guard

boolean canOpenForWrite(Context ctx, Uri uri) {
    try (OutputStream os = ctx.getContentResolver().openOutputStream(uri)) { return os != null; }
    catch (IOException | SecurityException e) { return false; }
}

Try / catch

try {
    saveIconTo(uri);
} catch (IOException e) {
    if (String.valueOf(e.getMessage()).contains("Unable to open output stream")) {
        requestNewDestinationUri(); // re-pick with SAF, or fall back to MediaStore
    } else {
        showGenericSaveError(e);
    }
}

Prevention

When it happens

Trigger: Calling the 'save icon' menu action with a SAF/document Uri whose provider returns null from openOutputStream: Uri points to a non-writable or removed document, provider crashed or revoked access, Uri was persisted but its permission was lost, or an invalid/unsupported scheme.

Common situations: Saving to a cloud/storage provider that's temporarily unavailable; document tree permissions revoked after an app update; target file deleted by another process between picking and saving; using a content:// Uri from a dead provider.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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