{"record":{"id":"722b1816c5ff2e07","repo":"TeamNewPipe/NewPipe","slug":"failed-to-create-the-tree-from-uri","errorCode":null,"errorMessage":"Failed to create the tree from Uri","messagePattern":"Failed to create the tree from Uri","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/org/schabi/newpipe/streams/io/StoredDirectoryHelper.java","lineNumber":73,"sourceCode":"        this.tag = tag;\n\n        if (ContentResolver.SCHEME_FILE.equalsIgnoreCase(path.getScheme())) {\n            ioTree = Paths.get(URI.create(path.toString()));\n            return;\n        }\n\n        this.context = context;\n\n        try {\n            this.context.getContentResolver().takePersistableUriPermission(path, PERMISSION_FLAGS);\n        } catch (final Exception e) {\n            throw new IOException(e);\n        }\n\n        this.docTree = DocumentFile.fromTreeUri(context, path);\n\n        if (this.docTree == null) {\n            throw new IOException(\"Failed to create the tree from Uri\");\n        }\n    }\n\n    public StoredFileHelper createFile(final String filename, final String mime) {\n        return createFile(filename, mime, false);\n    }\n\n    public StoredFileHelper createUniqueFile(final String name, final String mime) {\n        final List<String> matches = new ArrayList<>();\n        final String[] filename = splitFilename(name);\n        final String lcFileName = filename[0].toLowerCase();\n\n        if (docTree == null) {\n            try (Stream<Path> stream = Files.list(ioTree)) {\n                matches.addAll(stream.map(path -> path.getFileName().toString().toLowerCase())\n                        .filter(fileName -> fileName.startsWith(lcFileName))\n                        .collect(Collectors.toList()));\n            } catch (final IOException e) {","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/TeamNewPipe/NewPipe/blob/9e8be091560a69f35d44bd252eb00bc7911c977b/app/src/main/java/org/schabi/newpipe/streams/io/StoredDirectoryHelper.java#L55-L91","documentation":"Thrown by the StoredDirectoryHelper constructor for a SAF (Storage Access Framework) tree URI. After successfully calling takePersistableUriPermission, the constructor calls DocumentFile.fromTreeUri(context, path), which returns null when Android cannot resolve the URI to a document tree. The persistable-permission step succeeding does not guarantee fromTreeUri will resolve — a null result means the ContentProvider behind the URI does not expose a tree-document interface or the URI is malformed/expired despite the permission call.","triggerScenarios":"StoredDirectoryHelper is constructed with a content:// URI that is not a valid tree URI (not obtained from ACTION_OPEN_DOCUMENT_TREE), or a tree URI whose provider has been uninstalled/cleared, or a URI from a removed/reenmounted SD card. DocumentFile.fromTreeUri returns null only in these failure cases.","commonSituations":"User-selected download folder on a removable SD card that was unmounted; URI persisted across an app reinstall where the persistable permission was actually lost; a URI string saved/restored incorrectly (truncated or from a different user/SAF session); OEM ROM bug where the DocumentsProvider returns null for a valid tree URI.","solutions":["Re-prompt the user to pick the directory via ACTION_OPEN_DOCUMENT_TREE and store the freshly-granted URI.","Verify the URI scheme is content:// and that it was originally obtained from a tree picker before constructing StoredDirectoryHelper.","Check that the persistable permission is still held via getContentResolver().getPersistedUriPermissions() before reuse.","Catch the IOException and fall back to internal/external app-specific storage as a default download location."],"exampleFix":"// before\nStoredDirectoryHelper helper = new StoredDirectoryHelper(context, savedUri, tag);\n\n// after — verify the permission and URI are still valid before reuse\nList<UriPermission> perms = context.getContentResolver().getPersistedUriPermissions();\nboolean stillValid = perms.stream().anyMatch(p -> p.getUri().equals(savedUri));\nif (!stillValid) {\n    savedUri = requestNewTreeUri(); // re-launch ACTION_OPEN_DOCUMENT_TREE\n}\nStoredDirectoryHelper helper = new StoredDirectoryHelper(context, savedUri, tag);","handlingStrategy":"validation","validationCode":"// Before constructing StoredDirectoryHelper, confirm the tree URI is still valid:\nList<UriPermission> perms = context.getContentResolver().getPersistedUriPermissions();\nboolean valid = perms.stream().anyMatch(p -> p.getUri().equals(path) && p.isWritePermission());\nif (!valid) {\n    path = requestNewTree(); // re-launch ACTION_OPEN_DOCUMENT_TREE\n}","typeGuard":null,"tryCatchPattern":"try {\n    StoredDirectoryHelper helper = new StoredDirectoryHelper(context, uri, tag);\n} catch (IOException e) {\n    // tree URI invalid — prompt user to re-pick\n    showDirectoryPicker();\n}","preventionTips":["Re-verify persisted URI permissions before reusing a saved tree URI across sessions.","Always obtain tree URIs from ACTION_OPEN_DOCUMENT_TREE.","Handle the case where removable storage is unmounted by re-prompting for a directory."],"tags":["android-saf","storage","documentfile","uri-permission","io"],"backgroundTag":null,"analyzedSha":"9e8be091560a69f35d44bd252eb00bc7911c977b","analyzedAt":"2026-08-14T00:11:33.519Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}