Skip to content

fix(tui): port upstream failed RPC request reply (anomalyco/opencode#54936) - #1617

Open
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1793205780-rpc-failed-request
Open

devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1793205780-rpc-failed-request

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Issue for this PR

Closes #1616

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

The TUI waits on RPC calls to its server worker before it draws anything (e.g. client.call("server", network) for --port). When a worker handler threw, Rpc.listen never posted a reply and Rpc.client had no reject path, so the call never settled: altimate-code --port 4096 with the port taken is a blank terminal forever.

Ported from upstream anomalyco/opencode#54936:

  • Rpc.listen answers a failed request with { type: "rpc.result", error, id }. If the error has no message (Server.listen rejects with an empty ServeError that names the port in its cause), the cause's message is used.
  • Rpc.client tracks { resolve, reject } per call and rejects when the result carries an error string, so the failure reaches the TUI's existing error handling.

Wrapped in altimate_change markers tagged upstream_fix:. Successful calls are unchanged.

How did you verify your code works?

  • Two new tests in test/util/rpc.test.ts: the client rejects a call whose result carries an error, and listen answers a throwing handler (with cause fallback) and keeps answering later requests. Both fail on main and pass here; the existing 5 RPC tests still pass.
  • bun run typecheck in packages/opencode clean; bun run script/upstream/analyze.ts --markers --base origin/main --strict clean.

Cost side effects

None. No model calls, queries or resources added.

Screenshots / recordings

N/A, no UI change beyond the existing error message now being reachable.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR
  • I checked for cost side effects (model calls, queries or cloud resources that grow with user data) and described them, or wrote "none"

Written by Devin


Summary by cubic

Fixes the TUI hanging forever when an RPC worker handler throws, so --port with a port already taken now shows an error instead of a blank terminal.

Previously Rpc.listen never replied to a failed request and Rpc.client had no reject path, so the call never settled. Now listen answers failures with an error field (falling back to the error's cause message for empty errors like the port-in-use ServeError), and the client rejects on that error so the TUI's existing error handling runs. Successful calls are unchanged. Closes #1616.

Written for commit 8707ec3. Summary will update on new commits.

View guided diff Turn on auto-fix

@coderabbitai

coderabbitai Bot commented Oct 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 45255339-0bec-4c31-880a-f6d039885e2b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@dev-punia-altimate dev-punia-altimate left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Code Review — OpenCodeReview (Gemini) — 1 finding(s)

  • 1 anchored to a line (posted inline when the comment stream is on)
  • 0 without a line anchor
All findings (full text)

1. packages/opencode/src/util/rpc.ts (L68-L70)

[🟠 MEDIUM] If JSON.stringify(input) throws an error (e.g., due to circular references in the input argument), the Promise executor will correctly catch it and reject the promise. However, because pending.set was already called, the requestId will remain in the pending map indefinitely. Since the request is never actually sent via postMessage, a response will never arrive to clear it, resulting in a minor memory leak for that map entry.

Consider evaluating JSON.stringify before setting the request in the pending map. If serialization fails, the error will be caught by the Promise executor before the map is mutated, thus preventing the leak:

      return new Promise((resolve, reject) => {
        const payload = JSON.stringify({ type: "rpc.request", method, input, id: requestId })
        pending.set(requestId, { resolve, reject })
        target.postMessage(payload)
      })

Comment on lines +68 to +70
// altimate_change start — upstream_fix: answer a failed RPC request instead of hanging the caller (anomalyco/opencode#54936)
return new Promise((resolve, reject) => {
pending.set(requestId, { resolve, reject })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[🟠 MEDIUM] If JSON.stringify(input) throws an error (e.g., due to circular references in the input argument), the Promise executor will correctly catch it and reject the promise. However, because pending.set was already called, the requestId will remain in the pending map indefinitely. Since the request is never actually sent via postMessage, a response will never arrive to clear it, resulting in a minor memory leak for that map entry.

Consider evaluating JSON.stringify before setting the request in the pending map. If serialization fails, the error will be caught by the Promise executor before the map is mutated, thus preventing the leak:

      return new Promise((resolve, reject) => {
        const payload = JSON.stringify({ type: "rpc.request", method, input, id: requestId })
        pending.set(requestId, { resolve, reject })
        target.postMessage(payload)
      })

@dev-punia-altimate

Copy link
Copy Markdown
Contributor

🤖 Code Review — OpenCodeReview (Gemini) — No Issues Found

No supported files changed.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TUI hangs on a blank screen when a worker RPC request fails (e.g. --port already in use)

1 participant