nextcloud / nextcloud/android

Local folder picker crashes the app: UncheckedIOException from Files.list() escapes the IOException catch in FileHelper.listDirectoryEntries

オープン 初心者向け
#17,533 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る
0. Needs triage
主要言語
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

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。