{"record":{"id":"8d9348fbefd7fa24","repo":"MuntashirAkon/AppManager","slug":"unable-to-open-uri","errorCode":null,"errorMessage":"Unable to open URI","messagePattern":"Unable to open URI","errorType":"exception","errorClass":"java.io.IOException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/github/muntashirakon/AppManager/settings/crypto/ImportExportKeyStoreDialogFragment.java","lineNumber":50,"sourceCode":"import io.github.muntashirakon.AppManager.utils.ExUtils;\nimport io.github.muntashirakon.AppManager.utils.ThreadUtils;\nimport io.github.muntashirakon.AppManager.utils.UIUtils;\nimport io.github.muntashirakon.io.IoUtils;\n\npublic class ImportExportKeyStoreDialogFragment extends DialogFragment {\n    public static final String TAG = \"IEKeyStoreDialogFragment\";\n\n    private FragmentActivity mActivity;\n    private final ActivityResultLauncher<String> mExportKeyStore = registerForActivityResult(\n            new ActivityResultContracts.CreateDocument(\"application/octet-stream\"), uri -> {\n                if (uri == null) {\n                    dismiss();\n                    return;\n                }\n                ThreadUtils.postOnBackgroundThread(() -> {\n                    try (InputStream is = new FileInputStream(AM_KEYSTORE_FILE);\n                         OutputStream os = mActivity.getContentResolver().openOutputStream(uri)) {\n                        if (os == null) throw new IOException(\"Unable to open URI\");\n                        IoUtils.copy(is, os);\n                        ThreadUtils.postOnMainThread(() -> {\n                            UIUtils.displayShortToast(R.string.done);\n                            ExUtils.exceptionAsIgnored(this::dismiss);\n                        });\n                    } catch (IOException e) {\n                        ThreadUtils.postOnMainThread(() -> {\n                            UIUtils.displayShortToast(R.string.failed);\n                            ExUtils.exceptionAsIgnored(this::dismiss);\n                        });\n                    }\n                });\n            });\n    private final ActivityResultLauncher<String> mImportKeyStore = registerForActivityResult(\n            new ActivityResultContracts.GetContent(), uri -> {\n                if (uri == null) {\n                    dismiss();\n                    return;","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/io/github/muntashirakon/AppManager/settings/crypto/ImportExportKeyStoreDialogFragment.java#L32-L68","documentation":"In the key-store export flow, the dialog opens the user-picked destination URI via ContentResolver.openOutputStream. If the resolver returns null (no provider can write to that URI), an IOException 'Unable to open URI' is thrown and surfaced to the user as an error toast.","triggerScenarios":"Exporting the AM keystore with ACTION_CREATE_DOCUMENT when the selected target's provider returns a null OutputStream, e.g. an unexportable cloud account or a non-writable location.","commonSituations":"Choosing a cloud-storage document (Drive, etc.) that only grants read access, picking a location in a revoked/removed storage provider, or a stale URI from a previous session.","solutions":["Pick a local destination (Downloads/On-device storage) instead of a cloud provider for the export","Re-run the picker and select a fresh, writable location rather than reusing an old URI","Check result code and extras in onActivityResult; ensure the URI has FLAG_GRANT_READ/WRITE permissions","Catch the IOException and show a retry prompt instead of silently failing"],"exampleFix":"// before\nOutputStream os = mActivity.getContentResolver().openOutputStream(uri);\n// after\nOutputStream os = mActivity.getContentResolver().openOutputStream(uri, \"wt\");\nif (os == null) {\n    UIUtils.displayShortToast(R.string.failed);\n    return;\n}","handlingStrategy":"try-catch","validationCode":"// before export, verify the URI is writable\nString type = getContentResolver().getType(uri);\nif (type == null) { /* provider unknown; re-pick destination */ }","typeGuard":null,"tryCatchPattern":"try (OutputStream os = getContentResolver().openOutputStream(uri)) {\n    if (os == null) { /* show 'cannot write here' and re-pick */ return; }\n    IoUtils.copy(is, os);\n} catch (IOException e) {\n    UIUtils.displayShortToast(R.string.failed);\n}","preventionTips":["Prefer local destinations (Downloads) over cloud providers for exports","Re-pick URIs instead of reusing stale ones across sessions","Check openOutputStream()'s null return explicitly before copying","Show actionable error toasts when provider open fails"],"tags":["android","contentresolver","io"],"backgroundTag":"file-open-failed","analyzedSha":"0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5","analyzedAt":"2026-09-12T14:03:37.243Z","contentChangedAt":"2026-09-12T14:03:37.243Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}