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
- Re-pick the destination with the Storage Access Framework and retry the save
- Check persisted URI permissions (context.checkUriPermission / persistedUriPermissions) and re-request if lost
- Verify the Uri scheme is supported and the target document still exists before saving
- 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
- Re-check persisted URI permissions before writing
- Re-pick destination if the document may have been deleted
- Fall back to MediaStore/app-specific storage when a provider stream is unavailable
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
- Could not backup data
- Could not create directory named
- Could not resolve Uri:
- Could not open file for reading:
- Target is not backed by a real file
AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12).
Data as JSON: /api/errors/65555eac301f4d07.
Report an issue: GitHub.