[GH-ISSUE #3517] [Bug] JADX "Java heap space" load failure points to rea doctor, which reports healthy #1308

Closed
opened 2026-10-16 21:04:02 +00:00 by bsakaguchi · 2 comments
Owner

Originally created by @Rinkia on GitHub (Oct 14, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/3517

Before submitting

  • I searched the open and closed issues for a related report. Related: #3374 (doctor readiness for JADX). That check now exists in 6.5.0 but doesn't cover heap size.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Provider or capability discovery

Observed behavior

When JADX runs out of heap while loading an APK, REA returns:

{"code":"execution_failure","category":"execution_failure","retryable":false,
 "message":"Analysis could not complete. Run `rea doctor` and review the reported details; repeating the unchanged request is not expected to succeed.",
 "diagnostics":{"upstream_operation":"load_apk","reason":"load failed: Java heap space", ...}}

Under the same settings, rea doctor --provider jadx reports healthy: true with jadx: healthy, so following the remediation leads nowhere. The fix is the documented REA_JADX_HEAP_MIB setting (docs/android-analysis.md), but the error doesn't mention it.

REA already holds the evidence it would need. The error's partial observation includes the bridge's rea_jvm_status response with max_heap_bytes: 67108864, the heap that was too small.

Reproduction

Public APK: F-Droid de.markusfisch.android.binaryeye versionCode 180 (https://f-droid.org/repo/de.markusfisch.android.binaryeye_180.apk, sha256 568cec6598dbf42fbde9071af8747071ffea798473016a3d77b6ddb3bff944f1).

REA_JADX_HEAP_MIB=64 rea inspect-android-package /targets/binaryeye.apk --format json
REA_JADX_HEAP_MIB=64 rea doctor --provider jadx --format json
REA_JADX_HEAP_MIB inspect-android-package doctor --provider jadx
32 execution_failure, load failed: Java heap space —
64 execution_failure, load failed: Java heap space healthy: true
128 OK, 2,800 classes —

How it shows up without any explicit setting: REA supplies no default heap, so Java's ergonomics apply. In a container with --memory 3g, that gives JADX 768 MiB, which fails the same way on a large real-world APK (74 MB, about 28,000 classes; a private Play Store build, so not attached). REA_JADX_HEAP_MIB=4096 in a 6 GB container fixes it.

Expected behavior

  • Recognise Java heap space / OutOfMemoryError from JADX as a resource limit, not a generic execution failure.
  • Have the remediation name REA_JADX_HEAP_MIB (and JAVA_TOOL_OPTIONS), and report the observed heap from max_heap_bytes.
  • Optionally, have rea doctor --provider jadx report the effective JADX heap, so a too-small value shows up there.

Impact and scope

  • Deterministic for any APK larger than the effective heap. The default (a quarter of available memory) makes this likely for large apps in containers and on small CI runners.
  • Users and agents get a dead end: retrying is ruled out, doctor says healthy, and the actual setting isn't mentioned.
  • 6.4.0 had the same failure with a self-contradictory message ("Retry once…" while retryable: false). 6.5.0 fixed that contradiction but not the missing remediation. The message comes from src/domain/analysisErrorPresentation.ts:352.

REA version

rea-agents@6.5.0 (also seen on 6.4.0)

Runtime and platform

Linux x64 Docker container (Ubuntu 24.04) on Windows 11 / Docker Desktop. Node 24.21.0, JDK 21.0.12, jadx-headless-mcp 0.7.1.

Target and artifact context

Public F-Droid APK listed above. No private samples needed.

Interface

CLI (MCP shares the error projection; not separately run).

Originally created by @Rinkia on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3517 ### Before submitting - [x] I searched the open and closed issues for a related report. Related: #3374 (doctor readiness for JADX). That check now exists in 6.5.0 but doesn't cover heap size. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Provider or capability discovery ### Observed behavior When JADX runs out of heap while loading an APK, REA returns: ```json {"code":"execution_failure","category":"execution_failure","retryable":false, "message":"Analysis could not complete. Run `rea doctor` and review the reported details; repeating the unchanged request is not expected to succeed.", "diagnostics":{"upstream_operation":"load_apk","reason":"load failed: Java heap space", ...}} ``` Under the same settings, `rea doctor --provider jadx` reports `healthy: true` with `jadx: healthy`, so following the remediation leads nowhere. The fix is the documented `REA_JADX_HEAP_MIB` setting (`docs/android-analysis.md`), but the error doesn't mention it. REA already holds the evidence it would need. The error's partial observation includes the bridge's `rea_jvm_status` response with `max_heap_bytes: 67108864`, the heap that was too small. ### Reproduction Public APK: F-Droid `de.markusfisch.android.binaryeye` versionCode 180 (`https://f-droid.org/repo/de.markusfisch.android.binaryeye_180.apk`, sha256 `568cec6598dbf42fbde9071af8747071ffea798473016a3d77b6ddb3bff944f1`). ```bash REA_JADX_HEAP_MIB=64 rea inspect-android-package /targets/binaryeye.apk --format json REA_JADX_HEAP_MIB=64 rea doctor --provider jadx --format json ``` | `REA_JADX_HEAP_MIB` | `inspect-android-package` | `doctor --provider jadx` | |---|---|---| | 32 | `execution_failure`, `load failed: Java heap space` | — | | 64 | `execution_failure`, `load failed: Java heap space` | `healthy: true` | | 128 | OK, 2,800 classes | — | How it shows up without any explicit setting: REA supplies no default heap, so Java's ergonomics apply. In a container with `--memory 3g`, that gives JADX 768 MiB, which fails the same way on a large real-world APK (74 MB, about 28,000 classes; a private Play Store build, so not attached). `REA_JADX_HEAP_MIB=4096` in a 6 GB container fixes it. ### Expected behavior - Recognise `Java heap space` / `OutOfMemoryError` from JADX as a resource limit, not a generic execution failure. - Have the remediation name `REA_JADX_HEAP_MIB` (and `JAVA_TOOL_OPTIONS`), and report the observed heap from `max_heap_bytes`. - Optionally, have `rea doctor --provider jadx` report the effective JADX heap, so a too-small value shows up there. ### Impact and scope - Deterministic for any APK larger than the effective heap. The default (a quarter of available memory) makes this likely for large apps in containers and on small CI runners. - Users and agents get a dead end: retrying is ruled out, `doctor` says healthy, and the actual setting isn't mentioned. - 6.4.0 had the same failure with a self-contradictory message ("Retry once…" while `retryable: false`). 6.5.0 fixed that contradiction but not the missing remediation. The message comes from `src/domain/analysisErrorPresentation.ts:352`. ### REA version `rea-agents@6.5.0` (also seen on 6.4.0) ### Runtime and platform Linux x64 Docker container (Ubuntu 24.04) on Windows 11 / Docker Desktop. Node 24.21.0, JDK 21.0.12, `jadx-headless-mcp` 0.7.1. ### Target and artifact context Public F-Droid APK listed above. No private samples needed. ### Interface CLI (MCP shares the error projection; not separately run).
Author
Owner

@morluto commented on GitHub (Oct 17, 2026):

Fixed on main in 3fa972af7, with cleanup preservation in 9fa6d2b25.

The pinned upstream load_apk handler catches the actual exception and emits only its message. REA now translates the typed load outcome before that loss, retaining SessionHolder loading/locking. An actual OutOfMemoryError produces a named memory failure with observed Runtime.maxMemory() bytes. Known heap reasons name REA_JADX_HEAP_MIB/JVM options; other memory regions remain unknown. Arbitrary error text containing “Java heap space” stays a provider error. Doctor now states that configuration health does not establish APK memory capacity.

Reviewed related #3573. Its phrase matching is not integrated: failure text can contain caller-controlled paths and startup stderr can contain option echoes. Pre-handshake failures and other upstream stringified operations retain their existing diagnostics.

Validation: 113 focused tests across seven files passed; after final cleanup simplification, the three affected process suites passed again (57 tests). Pinned 0.7.1 JAR + JDK 21 controls cover typed failures, absent/empty reasons, ordinary phrase-bearing exceptions, immediate/suspended/inline completion, cancellation and downstream exceptions. These deliberately construct failures; they do not exhaust memory. Nine real CLI and nine real MCP inspections on a tiny authored APK passed with matching results and unchanged bytes. Cleanup failure/retry preserves the memory primary, shrinks current resource attribution and restores its remediation after recovery.

Six completed real MCP comparison workflows (three method inspections each, discovery excluded), baseline 63c65a224 versus final source: two alternating warm-repeat medians, elapsed 2.346/2.398 s, reaped-family CPU 3.519/3.593 s, sampled process-family RSS 847,773,696/850,092,032 bytes, cl100k output estimate 9,610.5/9,593.5 tokens. Normalized facts matched. No speedup claim; first processes were separate, true cold/input/catalog/whole-agent costs unmeasured, sampled RSS can miss transient or reparented processes. All six measured workspaces were physically absent afterwards, with no matching processes.

Limits: no actual heap exhaustion, reporter APK, Linux/Windows provider run, or severe-OOM response-delivery guarantee. Heavy profiling/stress stayed stopped. A partial decompiler from a failed upstream load remains inaccessible to this adapter; REA still retires the owned JVM before releasing its workspace.

<!-- gh-comment-id:6236988149 --> @morluto commented on GitHub (Oct 17, 2026): Fixed on main in 3fa972af7, with cleanup preservation in 9fa6d2b25. The pinned upstream `load_apk` handler catches the actual exception and emits only its message. REA now translates the typed load outcome before that loss, retaining `SessionHolder` loading/locking. An actual `OutOfMemoryError` produces a named memory failure with observed `Runtime.maxMemory()` bytes. Known heap reasons name `REA_JADX_HEAP_MIB`/JVM options; other memory regions remain unknown. Arbitrary error text containing “Java heap space” stays a provider error. Doctor now states that configuration health does not establish APK memory capacity. Reviewed related #3573. Its phrase matching is not integrated: failure text can contain caller-controlled paths and startup stderr can contain option echoes. Pre-handshake failures and other upstream stringified operations retain their existing diagnostics. Validation: 113 focused tests across seven files passed; after final cleanup simplification, the three affected process suites passed again (57 tests). Pinned 0.7.1 JAR + JDK 21 controls cover typed failures, absent/empty reasons, ordinary phrase-bearing exceptions, immediate/suspended/inline completion, cancellation and downstream exceptions. These deliberately construct failures; they do not exhaust memory. Nine real CLI and nine real MCP inspections on a tiny authored APK passed with matching results and unchanged bytes. Cleanup failure/retry preserves the memory primary, shrinks current resource attribution and restores its remediation after recovery. Six completed real MCP comparison workflows (three method inspections each, discovery excluded), baseline 63c65a224 versus final source: two alternating warm-repeat medians, elapsed 2.346/2.398 s, reaped-family CPU 3.519/3.593 s, sampled process-family RSS 847,773,696/850,092,032 bytes, cl100k output estimate 9,610.5/9,593.5 tokens. Normalized facts matched. No speedup claim; first processes were separate, true cold/input/catalog/whole-agent costs unmeasured, sampled RSS can miss transient or reparented processes. All six measured workspaces were physically absent afterwards, with no matching processes. Limits: no actual heap exhaustion, reporter APK, Linux/Windows provider run, or severe-OOM response-delivery guarantee. Heavy profiling/stress stayed stopped. A partial decompiler from a failed upstream load remains inaccessible to this adapter; REA still retires the owned JVM before releasing its workspace.
Author
Owner

@Rinkia commented on GitHub (Oct 20, 2026):

Confirmed on rea-agents@6.7.0 (Docker, Node 24.22.0, jadx-headless-mcp 0.7.1), with the same BinaryEye 180 APK (sha256 568cec65…f944f1) and command as the report. Thanks, morluto.

REA_JADX_HEAP_MIB inspect-android-package
32 resource_constraint; observed_max_heap_bytes: 33554432; exception_type: java.lang.OutOfMemoryError
64 resource_constraint; observed_max_heap_bytes: 67108864; same
128 OK, 2,800 classes

In both failures the remediation names REA_JADX_HEAP_MIB and JAVA_TOOL_OPTIONS, and says the observed heap is this run's limit, not what the APK needs. rea doctor --provider jadx now says it checks configuration, not whether an APK fits in memory, so the dead end from the report is gone.

<!-- gh-comment-id:6287660754 --> @Rinkia commented on GitHub (Oct 20, 2026): Confirmed on `rea-agents@6.7.0` (Docker, Node 24.22.0, jadx-headless-mcp 0.7.1), with the same BinaryEye 180 APK (sha256 `568cec65…f944f1`) and command as the report. Thanks, morluto. | `REA_JADX_HEAP_MIB` | `inspect-android-package` | |---|---| | 32 | `resource_constraint`; `observed_max_heap_bytes: 33554432`; `exception_type: java.lang.OutOfMemoryError` | | 64 | `resource_constraint`; `observed_max_heap_bytes: 67108864`; same | | 128 | OK, 2,800 classes | In both failures the remediation names `REA_JADX_HEAP_MIB` and `JAVA_TOOL_OPTIONS`, and says the observed heap is this run's limit, not what the APK needs. `rea doctor --provider jadx` now says it checks configuration, not whether an APK fits in memory, so the dead end from the report is gone.
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#1308
No description provided.