[GH-ISSUE #1978] [Bug] search_android_classes fails on APKs where JADX lists okio.SegmentedByteString twice #660

Closed
opened 2026-10-15 18:27:02 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @AntonioNoOne on GitHub (Oct 11, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/1978

Before submitting

  • I searched open and closed issues and pull requests for related work (#1850 is about inventory size, not this failure).
  • I removed credentials, private paths, and sensitive target data.

Area

Android providers (JADX), search_android_classes.

Observed behavior

search_android_classes fails on every query, including an empty one, with:

code: unreadable_output
reason: JADX class inventory is duplicated or differs from the loaded class count

Retrying gives the same result. The partial observation shows that the count is fine but one class name appears twice in the JADX inventory:

APK load_apk class_count inventory items unique names duplicated name
A 10402 10402 10401 okio.SegmentedByteString (x2)
B 10813 10813 10812 okio.SegmentedByteString (x2)

So the strict check in src/android/JadxAnalysis.ts (new Set(classes).size !== classes.length) rejects the whole inventory because of a single duplicate coming from the engine.

Reproduction

  1. Take an APK that bundles OkHttp/Okio (our two are Kotlin debug builds of the same app, built about a month apart).
  2. rea inspect-android-package <apk> works.
  3. rea search-android-classes <apk> <any query> fails as above, every time.

Expected behavior

A single duplicated name reported by JADX should not make the whole search unusable. For example: de-duplicate (while keeping the count check against class_count), or report the duplicate as a limitation in the Evidence instead of failing.

Impact and scope

Class search is the discovery step REA suggests when inspect-android-class / inspect-android-method cannot find a name ("use search_android_classes to discover fully qualified names"). With it failing, inner/synthetic classes (Kotlin coroutine bodies) cannot be discovered on these APKs. Okio is part of OkHttp, so many real-world Android apps may hit this.

Other Android commands worked well on the same APKs: inspect-android-package, inspect-android-class, inspect-android-method, trace-android-references.

REA version

rea-agents 6.4.0 (npm, latest at the time of the report).

Runtime and platform

Windows 11 x64, Node.js 24.15.0, Temurin JDK 17.0.20.1 (full JDK), jadx-headless-mcp 0.7.1 (-all.jar, SHA-256 6e5eacf5…ff75, matching JadxRelease.ts).

Interface

CLI (the same workflow is used by MCP).

Earliest failure stage

Result validation after the inventory pagination completes.

Analysis or suspected cause

The JADX engine lists okio.SegmentedByteString twice (likely the same class present in two places in the dex set). The duplicate check then turns an engine quirk into a hard failure.

Originally created by @AntonioNoOne on GitHub (Oct 11, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/1978 ### Before submitting - [x] I searched open and closed issues and pull requests for related work (#1850 is about inventory size, not this failure). - [x] I removed credentials, private paths, and sensitive target data. ### Area Android providers (JADX), `search_android_classes`. ### Observed behavior `search_android_classes` fails on every query, including an empty one, with: ``` code: unreadable_output reason: JADX class inventory is duplicated or differs from the loaded class count ``` Retrying gives the same result. The partial observation shows that the count is fine but **one class name appears twice** in the JADX inventory: | APK | `load_apk` class_count | inventory items | unique names | duplicated name | |---|---|---|---|---| | A | 10402 | 10402 | 10401 | `okio.SegmentedByteString` (x2) | | B | 10813 | 10813 | 10812 | `okio.SegmentedByteString` (x2) | So the strict check in `src/android/JadxAnalysis.ts` (`new Set(classes).size !== classes.length`) rejects the whole inventory because of a single duplicate coming from the engine. ### Reproduction 1. Take an APK that bundles OkHttp/Okio (our two are Kotlin debug builds of the same app, built about a month apart). 2. `rea inspect-android-package <apk>` works. 3. `rea search-android-classes <apk> <any query>` fails as above, every time. ### Expected behavior A single duplicated name reported by JADX should not make the whole search unusable. For example: de-duplicate (while keeping the count check against `class_count`), or report the duplicate as a limitation in the Evidence instead of failing. ### Impact and scope Class search is the discovery step REA suggests when `inspect-android-class` / `inspect-android-method` cannot find a name ("use search_android_classes to discover fully qualified names"). With it failing, inner/synthetic classes (Kotlin coroutine bodies) cannot be discovered on these APKs. Okio is part of OkHttp, so many real-world Android apps may hit this. Other Android commands worked well on the same APKs: `inspect-android-package`, `inspect-android-class`, `inspect-android-method`, `trace-android-references`. ### REA version rea-agents 6.4.0 (npm, latest at the time of the report). ### Runtime and platform Windows 11 x64, Node.js 24.15.0, Temurin JDK 17.0.20.1 (full JDK), jadx-headless-mcp 0.7.1 (`-all.jar`, SHA-256 `6e5eacf5…ff75`, matching `JadxRelease.ts`). ### Interface CLI (the same workflow is used by MCP). ### Earliest failure stage Result validation after the inventory pagination completes. ### Analysis or suspected cause The JADX engine lists `okio.SegmentedByteString` twice (likely the same class present in two places in the dex set). The duplicate check then turns an engine quirk into a hard failure.
bsakaguchi 2026-10-15 18:27:02 +00:00
Author
Owner

@CassieFox23 commented on GitHub (Oct 11, 2026):

TL;DR: Reproduced on Linux with three public F-Droid apps on rea-agents 6.4.0. JADX gives two different okio classes the same name. Main already counts the repeat instead of failing, but I did not test a release that has that fix.

Thanks, AntonioNoOne, for the clear report and the table.

Results

Class search on rea-agents 6.4.0 (a56f7bb66, the version in the report) and on main (89f5e489a). JDK 21, jadx-headless-mcp 0.7.1, same SHA-256 as the pin.

App (licence) JADX class count 6.4.0 search-android-classes main search-android-classes
AntennaPod 3.12.2 (GPL-3.0-only) 5,011 fails, exit 1, all 3 queries works, exit 0
Tasks.org (GPL-3.0-only) 15,676 fails, exit 1, all 3 queries works, exit 0
LibreTube (GPL-3.0-or-later) 4,544 fails, exit 1, all 3 queries works, exit 0
NewPipe (GPL-3.0-or-later) 7,096 works, exit 0 works, exit 0
  • The three queries were empty, okio and SegmentedByteString. inspect-android-package works on every app, on both builds.
  • The 6.4.0 failure matches the report. The code is unreadable_output. The reason is "JADX class inventory is duplicated or differs from the loaded class count".
  • The MCP tool fails the same way. The MCP server in my session is rea-agents 6.3.0, so that run says nothing about 6.4.0 or main.
  • JADX's raw list has as many items as its class count. Each failing app lists one name twice. The name is okio.SegmentedByteString. NewPipe has none.

What causes the duplicate

  • I read the class tables of each APK with a small Python parser. No app defines the same class twice.
  • AntennaPod, Tasks.org and LibreTube each hold two classes: okio/SegmentedByteString and okio/-SegmentedByteString. NewPipe holds only the first, and it passes.
  • JADX lists no class name with a leading dash. The other dash classes, like okio/-Base64, have no plain twin in the APK, and they show up in JADX's list with the dash removed. On main, inspect-android-class okio.internal.SegmentedByteString also finds a class that the APK only holds as okio/internal/-SegmentedByteString. So okio.-SegmentedByteString and okio.SegmentedByteString end up with the same name.
  • REA's own message agrees. On AntennaPod, both builds fail inspect-android-class okio.SegmentedByteString. The message says the class identity is ambiguous. It names both raw classes, with and without the dash.
  • So the guess in the report, the same class in two places in the dex set, is not what I saw. In LibreTube the two classes sit in different dex files, but in AntennaPod and Tasks.org they sit in the same one.
  • Why JADX drops the dash is unverified. My guess is that it renames names that are not valid Java identifiers. I did not read JADX's source.

Main and open PRs

  • Main has commit 067746887, which refers to #1978. It keeps repeated class rows and counts them. It is a direct commit with no PR, and it is not in 6.4.0.
  • On main, an empty query returns every class. Its duplicate_names field lists okio.SegmentedByteString with a count of 2. Searching for SegmentedByteString returns both rows. It also returns the class okio.internal.SegmentedByteString.
  • On main, inspect-android-class okio.SegmentedByteString still fails as ambiguous, and okio.-SegmentedByteString is "not found". I found no way to inspect those two classes by name.
  • PR #1984 adds one test on top of main (13 lines, in jadxIntegration.test.ts). Its description is out of date against its diff, so I did not rely on it.

Not run

  • JDK 17 and Windows. I used JDK 21 on Linux.
  • The reporter's two debug builds. These are release builds from F-Droid.
  • A published release that has the fix. The 6.5.0 release PR (#1693) was still open when I checked. I built main from source.
  • Main over MCP.
  • Why JADX's class count differs from the raw class table count (for AntennaPod, 5,011 against 12,065).
Commands
export PATH=~/.local/opt/node24/bin:$PATH
export JAVA_HOME=~/.local/opt/jdk-21.0.12.1+1
export REA_JADX_MCP_JAR=<path>/jadx-headless-mcp-0.7.1-all.jar
node <tree>/scripts/rea.mjs inspect-android-package <apk> --format json --full-output
node <tree>/scripts/rea.mjs search-android-classes <apk> "" --format json --full-output
node <tree>/scripts/rea.mjs search-android-classes <apk> okio --format json --full-output
node <tree>/scripts/rea.mjs search-android-classes <apk> SegmentedByteString --format json --full-output
node <tree>/scripts/rea.mjs inspect-android-class <apk> okio.SegmentedByteString --format json --full-output

<tree> was a git archive of rea-agents-6.4.0 and of main, each built with npm ci && npm run build. Exit codes are in the table above.

Test setup
  • Box: Nobara 44 (Fedora 44 base), Linux 7.2.9

  • CPU: AMD Ryzen 9 5900X, 12 cores / 24 threads

  • RAM: 32 GB DDR4-3600

  • Node: 24.18.0

  • JDK: OpenJDK 21.0.12.1 (the report used 17.0.20.1)

  • JADX: jadx-headless-mcp 0.7.1; SHA-256 6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75, same as the pin

  • Head: 89f5e489a (main)

  • Base: a56f7bb66, the rea-agents 6.4.0 release

  • The box was shared with other test agents while these ran.

  • AntennaPod 3.12.2, GPL-3.0-only

    • F-Droid download
    • SHA-256 3f43a4337a693cdb49141afe06cb596e88c796009ed77bff51e9444ce2d1ebfc
  • Tasks.org, GPL-3.0-only

    • F-Droid download
    • SHA-256 ed972cc1cec3456a11992435a3fcf132d1311f1e8dfd27758265122fbc206ce0
  • LibreTube, GPL-3.0-or-later

    • F-Droid download
    • SHA-256 792b1e37e7bd6a26d8da1c826a68677465c333633cb2198224cc96c84257d615
  • NewPipe, GPL-3.0-or-later

    • F-Droid download
    • SHA-256 1d66d19eedbab969b1be2e44e0ea35f5f8921cbe83b5c5ac810767ca1a84b7e8
  • The licences come from the F-Droid metadata (fdroiddata), not from the LICENSE files at each tag.

🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊

<!-- gh-comment-id:6114911703 --> @CassieFox23 commented on GitHub (Oct 11, 2026): **TL;DR:** Reproduced on Linux with three public F-Droid apps on rea-agents 6.4.0. JADX gives two different okio classes the same name. Main already counts the repeat instead of failing, but I did not test a release that has that fix. Thanks, AntonioNoOne, for the clear report and the table. ### Results Class search on rea-agents 6.4.0 (a56f7bb66, the version in the report) and on main (89f5e489a). JDK 21, jadx-headless-mcp 0.7.1, same SHA-256 as the pin. | App (licence) | JADX class count | 6.4.0 `search-android-classes` | main `search-android-classes` | |---|---|---|---| | AntennaPod 3.12.2 (GPL-3.0-only) | 5,011 | fails, exit 1, all 3 queries | works, exit 0 | | Tasks.org (GPL-3.0-only) | 15,676 | fails, exit 1, all 3 queries | works, exit 0 | | LibreTube (GPL-3.0-or-later) | 4,544 | fails, exit 1, all 3 queries | works, exit 0 | | NewPipe (GPL-3.0-or-later) | 7,096 | works, exit 0 | works, exit 0 | - The three queries were empty, `okio` and `SegmentedByteString`. `inspect-android-package` works on every app, on both builds. - The 6.4.0 failure matches the report. The code is `unreadable_output`. The reason is "JADX class inventory is duplicated or differs from the loaded class count". - The MCP tool fails the same way. The MCP server in my session is rea-agents 6.3.0, so that run says nothing about 6.4.0 or main. - JADX's raw list has as many items as its class count. Each failing app lists one name twice. The name is `okio.SegmentedByteString`. NewPipe has none. ### What causes the duplicate - I read the class tables of each APK with a small Python parser. No app defines the same class twice. - AntennaPod, Tasks.org and LibreTube each hold two classes: `okio/SegmentedByteString` and `okio/-SegmentedByteString`. NewPipe holds only the first, and it passes. - JADX lists no class name with a leading dash. The other dash classes, like `okio/-Base64`, have no plain twin in the APK, and they show up in JADX's list with the dash removed. On main, `inspect-android-class okio.internal.SegmentedByteString` also finds a class that the APK only holds as `okio/internal/-SegmentedByteString`. So `okio.-SegmentedByteString` and `okio.SegmentedByteString` end up with the same name. - REA's own message agrees. On AntennaPod, both builds fail `inspect-android-class okio.SegmentedByteString`. The message says the class identity is ambiguous. It names both raw classes, with and without the dash. - So the guess in the report, the same class in two places in the dex set, is not what I saw. In LibreTube the two classes sit in different dex files, but in AntennaPod and Tasks.org they sit in the same one. - Why JADX drops the dash is unverified. My guess is that it renames names that are not valid Java identifiers. I did not read JADX's source. ### Main and open PRs - Main has commit 067746887, which refers to #1978. It keeps repeated class rows and counts them. It is a direct commit with no PR, and it is not in 6.4.0. - On main, an empty query returns every class. Its `duplicate_names` field lists okio.SegmentedByteString with a count of 2. Searching for `SegmentedByteString` returns both rows. It also returns the class `okio.internal.SegmentedByteString`. - On main, `inspect-android-class okio.SegmentedByteString` still fails as ambiguous, and `okio.-SegmentedByteString` is "not found". I found no way to inspect those two classes by name. - PR #1984 adds one test on top of main (13 lines, in jadxIntegration.test.ts). Its description is out of date against its diff, so I did not rely on it. ### Not run - JDK 17 and Windows. I used JDK 21 on Linux. - The reporter's two debug builds. These are release builds from F-Droid. - A published release that has the fix. The 6.5.0 release PR (#1693) was still open when I checked. I built main from source. - Main over MCP. - Why JADX's class count differs from the raw class table count (for AntennaPod, 5,011 against 12,065). <details> <summary>Commands</summary> ```bash export PATH=~/.local/opt/node24/bin:$PATH export JAVA_HOME=~/.local/opt/jdk-21.0.12.1+1 export REA_JADX_MCP_JAR=<path>/jadx-headless-mcp-0.7.1-all.jar node <tree>/scripts/rea.mjs inspect-android-package <apk> --format json --full-output node <tree>/scripts/rea.mjs search-android-classes <apk> "" --format json --full-output node <tree>/scripts/rea.mjs search-android-classes <apk> okio --format json --full-output node <tree>/scripts/rea.mjs search-android-classes <apk> SegmentedByteString --format json --full-output node <tree>/scripts/rea.mjs inspect-android-class <apk> okio.SegmentedByteString --format json --full-output ``` `<tree>` was a `git archive` of rea-agents-6.4.0 and of main, each built with `npm ci && npm run build`. Exit codes are in the table above. </details> <details> <summary>Test setup</summary> - Box: Nobara 44 (Fedora 44 base), Linux 7.2.9 - CPU: AMD Ryzen 9 5900X, 12 cores / 24 threads - RAM: 32 GB DDR4-3600 - Node: 24.18.0 - JDK: OpenJDK 21.0.12.1 (the report used 17.0.20.1) - JADX: jadx-headless-mcp 0.7.1; SHA-256 `6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75`, same as the pin - Head: 89f5e489a (main) - Base: a56f7bb66, the rea-agents 6.4.0 release - The box was shared with other test agents while these ran. - AntennaPod 3.12.2, `GPL-3.0-only` - [F-Droid download](https://f-droid.org/repo/de.danoeh.antennapod_3120295.apk) - SHA-256 `3f43a4337a693cdb49141afe06cb596e88c796009ed77bff51e9444ce2d1ebfc` - Tasks.org, `GPL-3.0-only` - [F-Droid download](https://f-droid.org/repo/org.tasks_151202.apk) - SHA-256 `ed972cc1cec3456a11992435a3fcf132d1311f1e8dfd27758265122fbc206ce0` - LibreTube, `GPL-3.0-or-later` - [F-Droid download](https://f-droid.org/repo/com.github.libretube_72.apk) - SHA-256 `792b1e37e7bd6a26d8da1c826a68677465c333633cb2198224cc96c84257d615` - NewPipe, `GPL-3.0-or-later` - [F-Droid download](https://f-droid.org/repo/org.schabi.newpipe_1015.apk) - SHA-256 `1d66d19eedbab969b1be2e44e0ea35f5f8921cbe83b5c5ac810767ca1a84b7e8` - The licences come from the F-Droid metadata (fdroiddata), not from the LICENSE files at each tag. </details> 🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#660
No description provided.