Local folder picker crashes the app: UncheckedIOException from Files.list() escapes the IOException catch in FileHelper.listDirectoryEntries
- 主要言語
- Kotlin
- スター
- 5.6k
- フォーク
- 2k
- 平均マージ
- 2日 20時間
- マージ済み PR(30日)
- 105
説明
### ⚠️ Before posting ⚠️
- [x] This is a **bug**, not a question or an enhancement.
- [x] I've [searched for similar issues](https://github.com/nextcloud/android/issues) and didn't find a duplicate.
- [x] I've written a clear and descriptive title for this issue, not just "Bug" or "Crash".
- [x] I agree to follow Nextcloud's [Code of Conduct](https://nextcloud.com/contribute/code-of-conduct/).
### Steps to reproduce
1. Settings → Auto upload → ⋮ → "Set custom folder".
2. In the local folder picker, browse into a large directory under `/storage/emulated/0/Android/media/…` (in my case `com.whatsapp/WhatsApp/Media/WhatsApp Images`, ~11 600 files, and its `Sent` subdirectory).
3. Keep the device busy at the same time (several auto-upload rules were uploading in the background here).
The directory read fails part-way through iteration with `Software caused connection abort` from the emulated-storage layer. That failure is intermittent and I cannot trigger it on demand — but when it happens, the app dies instead of showing an empty/partial list.
### Expected behaviour
A failed directory read is logged and the picker shows what it could read (the `catch` in `FileHelper.listDirectoryEntries` is clearly written with exactly that intent).
### Actual behaviour
The whole app is killed. The exception escapes the background executor task, so there is no chance to recover.
The `catch` cannot fire, and this is visible in the source rather than inferred:
`FileHelper.listDirectoryEntries` (34.1.1) uses `Files.list(...)`, whose lazy stream reports directory-read failures from the terminal operation as **`java.io.UncheckedIOException`** — a `RuntimeException`, not an `IOException`:
```kotlin
return try {
Files.list(directory.toPath())
.map { it.toFile() }
…
.collect(Collectors.toList())
} catch (e: IOException) { // never reached for UncheckedIOException
Log_OC.d(TAG, "listDirectoryEntries: $e")
emptyList()
}
```
`LocalFileListAdapter.loadRemainingEntries()` calls it on a pool thread with no guard, so the `UncheckedIOException` terminates the thread and takes the process with it.
Two small notes that may save reviewer time:
* Adding `catch (e: UncheckedIOException)` (or catching its `cause`) next to the existing `IOException` branch restores the intended behaviour.
* `FileHelper` on master has since been rewritten to `forEachDirectoryPage` using `Files.newDirectoryStream` with `catch (IOException)` + `catch (DirectoryIteratorException)`, which does cover the iteration failure. So master may already be fine and this report is mainly about the released 34.1.1 line — worth confirming whether a backport is wanted.
### Android version
17
### Device brand and model
Samsung SM-F976B (Galaxy Z Fold8), stock
### Stock or custom OS?
Stock
### Nextcloud android app version
34.1.1 (340010190, gplay)
### Nextcloud server version
31.0.14
### Using a reverse proxy?
Yes
### Android logs
### Cause of error
```java
Exception in thread "pool-27-thread-1" java.io.UncheckedIOException: java.nio.file.FileSystemException: /storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/WhatsApp Images/Sent: Software caused connection abort
at java.nio.file.Files$2.hasNext(Files.java:3729)
at java.util.Spliterators$IteratorSpliterator.tryAdvance(Spliterators.java:1949)
at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:156)
at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:592)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:578)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:564)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:267)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:748)
at com.nextcloud.utils.FileHelper.listDirectoryEntries(FileHelper.kt:31)
at com.owncloud.android.ui.adapter.LocalFileListAdapter.loadRemainingEntries(LocalFileListAdapter.java:434)
at com.owncloud.android.ui.adapter.LocalFileListAdapter.lambda$swapDirectory$2(LocalFileListAdapter.java:405)
at com.owncloud.android.ui.adapter.LocalFileListAdapter.$r8$lambda$EFQ1jO9hT3yhM5CFqUk4qO5Vyvg(LocalFileListAdapter.java:0)
at com.owncloud.android.ui.adapter.LocalFileListAdapter$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1100)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:1572)
Caused by: java.nio.file.FileSystemException: /storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/WhatsApp Images/Sent: Software caused connection abort
at sun.nio.fs.UnixDirectoryStream$UnixDirectoryIterator.readNextEntry(UnixDirectoryStream.java:180)
at sun.nio.fs.UnixDirectoryStream$UnixDirectoryIterator.hasNext(UnixDirectoryStream.java:210)
at java.nio.file.Files$2.hasNext(Files.java:3727)
…
```
### App information
* ID: `com.nextcloud.client`
* Version: `340010190`
* Build flavor: `gplay`
### Device information
* Brand: `samsung`
* Device: `q8q`
* Model: `SM-F976B`
* Id: `CP2A.260605.016`
* Product: `q8qxeea`
### Firmware
* SDK: `37`
* Release: `17`
* Incremental: `F976BXXS2AZH7`
コントリビューションガイド
調査の方向性
FileHelper.kt の listDirectoryEntries から開始し、Files.list() が終端イテレーション中に失敗をどのように報告するかを確認します。次に、LocalFileListAdapter.kt の loadRemainingEntries() で、バックグラウンドタスクとの境界を確認します。ディレクトリの読み取りに失敗してもアプリが終了しなくなっていること、picker が読み取り可能なエントリを保持するか空のリストを表示すること、そしてこの修正を 34.1.1 系列にバックポートする必要があるかを確認します。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- kotlin
- 領域
- mobile
- issue の種類
- バグ
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 活発さ
- 活発
- 明瞭さ
- 明確に書かれている
- 初心者へのやさしさ
- 84/100