[GH-ISSUE #5264] [Bug] Setup pins JAVA_HOME to a versioned Homebrew Cellar path that breaks after a JDK upgrade #3216

Closed
opened 2026-10-31 00:37:57 +00:00 by bsakaguchi · 2 comments
Owner

Originally created by @monsterlag on GitHub (Oct 19, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/5264

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Setup, packaging, or upgrade

Observed behavior

On macOS with Homebrew's keg-only openjdk@21, rea setup --client codex writes JAVA_HOME into the registration's env as the versioned Cellar path, for example /opt/homebrew/Cellar/openjdk@21/21.0.12.1/libexec/openjdk.jdk/Contents/Home. It does this even when the caller's JAVA_HOME, and the existing registration, use Homebrew's stable opt path, /opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home. The plan says "detected values replace matching keys", so the stable path is overwritten.

Homebrew removes the old Cellar directory on the next brew upgrade openjdk@21 (after brew cleanup). The registered JAVA_HOME then no longer exists, and the Ghidra provider fails when the agent starts REA. Nothing reports it until analysis is attempted.

Reproduction

trial="$(mktemp -d)"; mkdir -p "$trial/.codex"
cat > "$trial/.codex/config.toml" <<'TOML'
[mcp_servers.rea]
command = "/opt/homebrew/bin/rea"
args = ["mcp"]
startup_timeout_sec = 30
tool_timeout_sec = 450

[mcp_servers.rea.env]
GHIDRA_INSTALL_DIR = "/opt/homebrew/opt/ghidra/libexec"
JAVA_HOME = "/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home"
TOML
cp "$trial/.codex/config.toml" "$trial/before.toml"
(cd "$trial" && HOME="$trial" CODEX_HOME="$trial/.codex" \
  GHIDRA_INSTALL_DIR=/opt/homebrew/opt/ghidra/libexec \
  JAVA_HOME=/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home \
  rea setup --client codex --skill=false --yes >/dev/null)
diff "$trial/before.toml" "$trial/.codex/config.toml"
# < JAVA_HOME = "/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home"
# > JAVA_HOME = "/opt/homebrew/Cellar/openjdk@21/21.0.12.1/libexec/openjdk.jdk/Contents/Home"

Expected behavior

Keep the path the user or caller supplied when it resolves to the same JDK, or prefer Homebrew's opt symlink over the realpath. rea doctor already accepts the opt path, so only setup's resolution step needs to change. This is the same class of problem as #4847 (a version-specific path that breaks after an upgrade), but for the provider environment instead of the launcher command.

Impact and scope

macOS users with Homebrew JDKs (keg-only openjdk@21 is the documented route; see #4556/#4657). Each JDK patch upgrade silently breaks Ghidra for MCP clients registered by setup. Users who pin the opt path by hand can't safely re-run rea setup or let an updater refresh the registration. Verified for the Codex registration; the other clients use the same provider environment detection, so they may be affected too (not verified).

REA version

6.7.0 (npm global)

Runtime and platform

macOS 27.0.1 arm64, Node v26.12.0, Homebrew openjdk@21 21.0.12.1, Ghidra 12.1.4 (Homebrew)

Originally created by @monsterlag on GitHub (Oct 19, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5264 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Setup, packaging, or upgrade ### Observed behavior On macOS with Homebrew's keg-only `openjdk@21`, `rea setup --client codex` writes `JAVA_HOME` into the registration's `env` as the **versioned Cellar path**, for example `/opt/homebrew/Cellar/openjdk@21/21.0.12.1/libexec/openjdk.jdk/Contents/Home`. It does this even when the caller's `JAVA_HOME`, and the existing registration, use Homebrew's stable `opt` path, `/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home`. The plan says "detected values replace matching keys", so the stable path is overwritten. Homebrew removes the old Cellar directory on the next `brew upgrade openjdk@21` (after `brew cleanup`). The registered `JAVA_HOME` then no longer exists, and the Ghidra provider fails when the agent starts REA. Nothing reports it until analysis is attempted. ### Reproduction ```sh trial="$(mktemp -d)"; mkdir -p "$trial/.codex" cat > "$trial/.codex/config.toml" <<'TOML' [mcp_servers.rea] command = "/opt/homebrew/bin/rea" args = ["mcp"] startup_timeout_sec = 30 tool_timeout_sec = 450 [mcp_servers.rea.env] GHIDRA_INSTALL_DIR = "/opt/homebrew/opt/ghidra/libexec" JAVA_HOME = "/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home" TOML cp "$trial/.codex/config.toml" "$trial/before.toml" (cd "$trial" && HOME="$trial" CODEX_HOME="$trial/.codex" \ GHIDRA_INSTALL_DIR=/opt/homebrew/opt/ghidra/libexec \ JAVA_HOME=/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home \ rea setup --client codex --skill=false --yes >/dev/null) diff "$trial/before.toml" "$trial/.codex/config.toml" # < JAVA_HOME = "/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home" # > JAVA_HOME = "/opt/homebrew/Cellar/openjdk@21/21.0.12.1/libexec/openjdk.jdk/Contents/Home" ``` ### Expected behavior Keep the path the user or caller supplied when it resolves to the same JDK, or prefer Homebrew's `opt` symlink over the realpath. `rea doctor` already accepts the `opt` path, so only setup's resolution step needs to change. This is the same class of problem as #4847 (a version-specific path that breaks after an upgrade), but for the provider environment instead of the launcher command. ### Impact and scope macOS users with Homebrew JDKs (keg-only `openjdk@21` is the documented route; see #4556/#4657). Each JDK patch upgrade silently breaks Ghidra for MCP clients registered by setup. Users who pin the `opt` path by hand can't safely re-run `rea setup` or let an updater refresh the registration. Verified for the Codex registration; the other clients use the same provider environment detection, so they may be affected too (not verified). ### REA version 6.7.0 (npm global) ### Runtime and platform macOS 27.0.1 arm64, Node v26.12.0, Homebrew openjdk@21 21.0.12.1, Ghidra 12.1.4 (Homebrew)
Author
Owner

@akushonkamen commented on GitHub (Oct 19, 2026):

Claiming this one. I'll fix setup's JAVA_HOME resolution so it preserves the caller's/opt path when it resolves to the same JDK, with unit tests covering the Homebrew keg-only case.

<!-- gh-comment-id:6271535569 --> @akushonkamen commented on GitHub (Oct 19, 2026): Claiming this one. I'll fix setup's JAVA_HOME resolution so it preserves the caller's/opt path when it resolves to the same JDK, with unit tests covering the Homebrew keg-only case.
Author
Owner

@akushonkamen commented on GitHub (Oct 19, 2026):

Fixed on branch fix/5264-setup-java-home-opt-path (github.com/akushonkamen/rea@a4bb4de97, based on current main).

Setup now adopts the JVM-reported java.home only through a stability step in src/ghidra/GhidraInstallation.ts (stableJavaHome): if the caller's JAVA_HOME resolves (realpath) to the observed JDK, the caller's spelling is kept; otherwise a versioned Homebrew Cellar home is rewritten to the formula's stable opt symlink when both resolve to the same JDK, and non-Homebrew homes are left untouched.

Verified with new unit tests in src/ghidra/GhidraInstallation.test.ts (caller opt path preserved, Cellar→opt rewrite, non-Homebrew unchanged): npx vitest run src/ghidra/GhidraInstallation.test.ts is 95/95 green, plus src/application/Setup.planning.test.ts, src/ghidra/GhidraDoctor.test.ts (136/136) and the ghidra boundary suite (159 passed). A full end-to-end run of the issue's repro against a real Homebrew openjdk@21 wasn't possible on this machine (no Homebrew JDK installed), so the Homebrew layout is covered by tests using a real keg-only symlink layout.

I'll open the PR once the currently blocked PR-creation path is available.

<!-- gh-comment-id:6272147025 --> @akushonkamen commented on GitHub (Oct 19, 2026): Fixed on branch `fix/5264-setup-java-home-opt-path` (https://github.com/akushonkamen/rea/commit/a4bb4de97, based on current `main`). `Setup` now adopts the JVM-reported `java.home` only through a stability step in `src/ghidra/GhidraInstallation.ts` (`stableJavaHome`): if the caller's `JAVA_HOME` resolves (realpath) to the observed JDK, the caller's spelling is kept; otherwise a versioned Homebrew Cellar home is rewritten to the formula's stable `opt` symlink when both resolve to the same JDK, and non-Homebrew homes are left untouched. Verified with new unit tests in `src/ghidra/GhidraInstallation.test.ts` (caller opt path preserved, Cellar→opt rewrite, non-Homebrew unchanged): `npx vitest run src/ghidra/GhidraInstallation.test.ts` is 95/95 green, plus `src/application/Setup.planning.test.ts`, `src/ghidra/GhidraDoctor.test.ts` (136/136) and the ghidra boundary suite (159 passed). A full end-to-end run of the issue's repro against a real Homebrew openjdk@21 wasn't possible on this machine (no Homebrew JDK installed), so the Homebrew layout is covered by tests using a real keg-only symlink layout. I'll open the PR once the currently blocked PR-creation path is available.
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#3216
No description provided.