MuntashirAkon/AppManager · error · IOException
Could not fetch attributes for tree ${documentUri}
Error message
Could not fetch attributes for tree ${documentUri} What it means
PathAttributesImpl.fromSaf queries the DocumentsProvider backing the given SAF DocumentFile for its metadata columns. If the returned cursor is empty (no first row), the provider has no record for this tree/document URI, so the library throws this IOException instead of returning partial attributes. It signals that the URI is unresolvable — usually deleted, revoked, or malformed.
Source
Thrown at app/src/main/java/io/github/muntashirakon/io/PathAttributesImpl.java:47
return new PathAttributesImpl(f.getName(), file.getType(), f.lastModified(), f.lastAccess(), f.creationTime(),
OsConstants.S_ISREG(mode), OsConstants.S_ISDIR(mode), OsConstants.S_ISLNK(mode), f.length());
}
@NonNull
public static PathAttributesImpl fromVirtual(@NonNull VirtualDocumentFile file) {
int mode = file.getMode();
return new PathAttributesImpl(file.getName(), file.getType(), file.lastModified(), file.lastAccess(), file.creationTime(),
OsConstants.S_ISREG(mode), OsConstants.S_ISDIR(mode), OsConstants.S_ISLNK(mode), file.length());
}
@NonNull
public static PathAttributesImpl fromSaf(@NonNull Context context, @NonNull DocumentFile safDocumentFile)
throws IOException {
Uri documentUri = safDocumentFile.getUri();
ContentResolver resolver = context.getContentResolver();
try (Cursor c = resolver.query(documentUri, null, null, null, null)) {
if (!c.moveToFirst()) {
throw new IOException("Could not fetch attributes for tree " + documentUri);
}
String[] columns = c.getColumnNames();
String name = null;
String type = null;
long lastModified = 0;
long size = 0;
for (int i = 0; i < columns.length; ++i) {
switch (columns[i]) {
case DocumentsContract.Document.COLUMN_DISPLAY_NAME:
name = c.getString(i);
break;
case DocumentsContract.Document.COLUMN_MIME_TYPE:
type = c.getString(i);
break;
case DocumentsContract.Document.COLUMN_LAST_MODIFIED:
lastModified = c.getLong(i);
break;
case DocumentsContract.Document.COLUMN_SIZE:View on GitHub (pinned to 0152f468fc)
Solutions
- Re-acquire the tree/document via the Storage Access Framework picker (ACTION_OPEN_DOCUMENT_TREE / ACTION_OPEN_DOCUMENT) and rebuild the DocumentFile
- Check that you still hold the URI permission (context.contentResolver.persistedUriPermissions) and re-take it if revoked
- Verify the document still exists via DocumentFile.exists() before querying attributes
- Wrap the call in try-catch for IOException and treat it as a 'missing document' case rather than retrying with the same stale URI
Example fix
// before
PathAttributesImpl attrs = PathAttributesImpl.fromSaf(context, staleDocFile);
// after
if (!staleDocFile.exists() || !hasUriPermission(context, staleDocFile.getUri())) {
staleDocFile = rePickTreeViaSaf(); // ACTION_OPEN_DOCUMENT_TREE
}
PathAttributesImpl attrs = PathAttributesImpl.fromSaf(context, staleDocFile); Defensive patterns
Strategy: try-catch
Validate before calling
if (docFile == null || !docFile.exists() || !hasPersistedPermission(context, docFile.getUri())) { reAcquireTree(); } Type guard
boolean isResolvableTree(Context ctx, DocumentFile f) {
return f != null && f.getUri() != null
&& ctx.getContentResolver().getPersistedUriPermissions().stream()
.anyMatch(p -> p.getUri().equals(f.getUri()));
} Try / catch
try {
PathAttributesImpl attrs = PathAttributesImpl.fromSaf(context, docFile);
} catch (IOException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Could not fetch attributes for tree")) {
docFile = rePickTreeViaSaf(); // URI dead or revoked
} else throw e;
} Prevention
- Persist URI permissions with takePersistableUriPermission immediately after the SAF picker returns
- Check DocumentFile.exists() before querying metadata
- Treat any stored tree URI as volatile and re-validate on app start
- Handle provider availability for third-party/cloud DocumentsProviders
When it happens
Trigger: Calling Path.getAttributes()/fromSaf with a DocumentFile whose tree URI no longer resolves: the document was deleted or moved, the persisted URI permission was revoked (e.g. after app data clear or reboot on some devices), or the provider returned a valid-but-empty cursor.
Common situations: Persisted ACTION_OPEN_DOCUMENT_TREE URIs pointing at storage roots that were re-mounted or wiped; third-party DocumentsProviders (cloud providers) being offline; testing with a fabricated Uri that was never returned by the SAF picker.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
- Unable to open output stream.
- Current file is not a directory.
- Could not create directory named ${displayName}
- Directory
- Could not create
AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12).
Data as JSON: /api/errors/d27bcc3d6d466f92.
Report an issue: GitHub.