{"record":{"id":"65555eac301f4d07","repo":"MuntashirAkon/AppManager","slug":"unable-to-open-output-stream","errorCode":null,"errorMessage":"Unable to open output stream.","messagePattern":"Unable to open output stream\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/github/muntashirakon/AppManager/details/info/AppInfoFragment.java","lineNumber":610,"sourceCode":"                        int flags = 0;\n                        for (int flag : selections) {\n                            flags |= flag;\n                        }\n                        NetworkPolicyManagerCompat.setUidPolicy(mApplicationInfo.uid, flags);\n                        mMainModel.getTagsAlteredLiveData().setValue(true);\n                    })\n                    .show();\n        } else if (itemId == R.id.action_extract_icon) {\n            String iconName = mAppLabel + \"_icon.png\";\n            mExport.launch(iconName, uri -> {\n                if (uri == null) {\n                    // Back button pressed.\n                    return;\n                }\n                ThreadUtils.postOnBackgroundThread(() -> {\n                    try (OutputStream outputStream = Paths.get(uri).openOutputStream()) {\n                        if (outputStream == null) {\n                            throw new IOException(\"Unable to open output stream.\");\n                        }\n                        Bitmap bitmap = getBitmapFromDrawable(mApplicationInfo.loadIcon(mPackageManager));\n                        bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);\n                        outputStream.flush();\n                        ThreadUtils.postOnMainThread(() -> displayShortToast(R.string.saved_successfully));\n                    } catch (IOException e) {\n                        Log.e(TAG, e);\n                        ThreadUtils.postOnMainThread(() -> displayShortToast(R.string.saving_failed));\n                    }\n                });\n            });\n        } else if (itemId == R.id.action_install) {\n            List<UserInfo> users = Users.getUsers();\n            CharSequence[] userNames = new String[users.size()];\n            int i = 0;\n            for (UserInfo info : users) {\n                userNames[i++] = info.toLocalizedString(requireContext());\n            }","sourceCodeStart":592,"sourceCodeEnd":628,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/io/github/muntashirakon/AppManager/details/info/AppInfoFragment.java#L592-L628","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nOutputStream os = Paths.get(uri).openOutputStream(); // may return null -> IOException\n// after\nOutputStream os = Paths.get(uri).openOutputStream();\nif (os == null) {\n    // re-pick destination or fall back\n    requestNewDestinationUri();\n    return;\n}","handlingStrategy":"try-catch","validationCode":"OutputStream os = Paths.get(uri).openOutputStream();\nboolean writable = os != null;\nif (os != null) os.close();","typeGuard":"boolean canOpenForWrite(Context ctx, Uri uri) {\n    try (OutputStream os = ctx.getContentResolver().openOutputStream(uri)) { return os != null; }\n    catch (IOException | SecurityException e) { return false; }\n}","tryCatchPattern":"try {\n    saveIconTo(uri);\n} catch (IOException e) {\n    if (String.valueOf(e.getMessage()).contains(\"Unable to open output stream\")) {\n        requestNewDestinationUri(); // re-pick with SAF, or fall back to MediaStore\n    } else {\n        showGenericSaveError(e);\n    }\n}","preventionTips":["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"],"tags":["android","io","storage","saf"],"backgroundTag":"file-write-failed","analyzedSha":"0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5","analyzedAt":"2026-09-12T14:03:37.243Z","contentChangedAt":"2026-09-12T14:03:37.243Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}