Skip to content

Conversation

@well0nez
Copy link

Summary

This PR fixes several memory leaks and hanging issues in OpenCode:

RPC Error Propagation (Hang Prevention)

  • rpc.ts: Server-side errors now properly propagate back to client via rpc.error messages
  • rpc.ts: Added rejectAll() method to reject all pending requests on worker crash
  • rpc.ts: Added 60s timeout for RPC requests to prevent infinite hangs
  • thread.ts: Worker crash now calls rejectAll() to fail all pending RPCs

Memory Leak Fixes

  • acp.ts: Consolidated stdin listeners (was creating new listener per message)
  • mcp/index.ts: Cleanup stdout/stderr listeners when subprocess closes
  • mdns.ts: Track and cleanup service listeners on stop
  • worker.ts: Cleanup GlobalBus listener on worker shutdown
  • thread.ts: Cleanup terminal on worker crash, add SIGINT/SIGTERM handlers

Event Stream Resilience

  • worker.ts: Added error logging, consecutive error tracking, and exponential backoff

SDK Timeout Protection

  • agent.ts, session.ts, run.ts: Wrap SDK calls with 60s timeout to prevent indefinite hangs

Root Cause

The main hanging issue was caused by RPC having NO error propagation - when worker-side code threw an exception, the error was never sent back to the client, causing await calls to hang indefinitely.

Testing

  • TypeScript compilation passes
  • Manual testing of TUI startup/shutdown cycles

OpenCode Dev added 7 commits January 27, 2026 14:04
- Add server-side error handling that sends rpc.error messages to client
- Add client-side handling of rpc.error to reject pending promises
- Add rejectAll() method to reject all pending requests on worker crash
- Add 60s timeout for RPC requests to prevent infinite hangs
- Add MAX_PENDING_REQUESTS limit (1000) as a safety guard

This fixes hangs where worker-side exceptions were never propagated
back to the client, causing await calls to hang indefinitely.
- Call client.rejectAll() on worker error to fail pending RPCs
- Add terminal cleanup on worker crash to prevent leaked handles
- Add SIGINT/SIGTERM handlers to cleanup before exit
- Add error logging and consecutive error tracking for event stream
- Add exponential backoff on repeated failures
- Cleanup GlobalBus listener on worker shutdown
- Use single stdin listener instead of creating new ones per message
- Properly cleanup listener on process exit
- Remove stdout/stderr listeners when subprocess closes
- Prevents listener accumulation on repeated MCP restarts
- Track and remove listeners when mDNS service stops
- Prevents listener leaks on repeated start/stop cycles
- Wrap SDK calls with 60s timeout to prevent indefinite hangs
- Applies to agent.ts, session.ts, and run.ts
- Uses withTimeout utility for consistent timeout handling
@github-actions
Copy link
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions
Copy link
Contributor

The following comment was made by an LLM, it may be inaccurate:

Found a potential related PR:

PR #10392: fix(opencode): memory leaks in session handling
#10392

Why it might be related:

Please review PR #10392 to determine if it overlaps with the current PR's session-related memory leak fixes.

@well0nez well0nez closed this Jan 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant