Repository navigation
Description
Description
Repro (v2.0.23, macOS arm64):
- Start
opencode serve, thenPOST /api/ptyand connect a client to/api/pty/:id/connect. - Send
yes\nas input.
Within a few seconds the server sits at about 150% CPU and its memory grows past 2–7 GB. Ctrl-C (\x03) sent over the socket gets no response within 30 seconds. After the client process exits, the server stays running and orphaned, still at full CPU, and so does yes. That happens even for serve --stdio, whose stdin is closed when the client exits.
Causes (packages/core/src/pty.ts, packages/server/src/handlers/pty.ts):
-
Copying the retained buffer on every chunk.
session.buffer += chunk, followed bysession.buffer.slice(excess)once the buffer passesBUFFER_LIMIT(2 MB). From then on, every chunk copies about 2 MB. Under a flood that becomes the main CPU cost. Suggested fix: keep retained output as a list of chunks with a running length, and drop whole chunks from the front (slice only the first one). -
No backpressure to the PTY.
onDatapushes into each subscriber'sQueue.unboundedoutbox. Nothing slows the PTY down when a socket can't keep up, so memory grows without limit. Suggested fix: cap each subscriber's outbox. Past a high-water mark, pause reading from the PTY and resume once the queue drains. VS Code and xterm.js handle this with byte acknowledgements from the client. Dropping a slow subscriber, or making it resync from the buffer, also works. -
Input starved. With the event loop busy relaying output, input from the socket is handled too late to interrupt anything. Fixing 1 and 2 should cover this, but it's worth a test: run
yes, send Ctrl-C, and expect the prompt back within a second. -
Orphaned server and children. A flooded
serve --stdiomisses stdin EOF and keeps running with its PTY children. On stdin EOF, the server should kill its PTY processes and exit, even when busy.
Smaller gaps:
- Attaching to an exited PTY closes with
4404 session exited, even though its output is retained (pty.listdocuments exited sessions as retained). Clients can't show the last output of a finished command. - PTYs belong to a location, and a location is evicted 60 minutes after its last session event (
location-activity.ts). PTY traffic doesn't count, so a terminal in a folder with no agent activity is killed without warning. - A PTY inherits the server's full environment, and there's no plugin hook like
shellcreate.beforeto clean it. Clients have to wrap the command to remove server-only variables and credentials. Pty.CreateInputhas no size field, so every client needs a second request to set rows and columns.
Plugins
No response
OpenCode version
2.0.23
Steps to reproduce
- open OpenCode TUI
- launch a session
- open integrated terminal
- type
yesand enter - observe the balloon
Screenshot and/or share link
No response
Operating System
macOS 26
Terminal
ghostty
Activity
github-actions commentedon Oct 6, 2026
This issue might be a duplicate of existing issues. Please check:
opencode serveleaves orphaned PTY shell processes when PTY connection is denied; this issue also reports orphaned PTY children, though the trigger differs.opencodejr commentedon Oct 27, 2026
Thanks for the detailed report. I reproduced this on 2.0.23 using the server API, and it still happens on the latest
dev(f7d0ce1).The script below creates a PTY with
POST /api/pty, connects over the WebSocket, sendsyes, and samples the server process for 10 seconds:devto about 1 GB.dev) of output, so the rest was piling up on the server.echotook about 20 s to come back on 2.0.23 and about 12 s ondev. The server stayed around 100% CPU afterwards.This matches your first three points: the retained buffer is re-sliced on every chunk once it passes 2 MB, and output goes into an unbounded queue for each subscriber with nothing slowing the PTY down. I didn't reproduce the orphaned
serve --stdiocase: in my non-stdio test,yesexited once the client closed. The smaller gaps you listed (attaching to exited PTYs, location eviction, environment cleanup, size on create) read more like separate feature requests.The linked issue search didn't work during triage, so I couldn't check for existing duplicates.
Reproduction script (pty-flood.ts)
Run it:
bun pty-flood.ts