anomalyco / anomalyco/opencode
tui: nested Mermaid states cause infinite allocation and OOM
@simonklee is already working on this.
Since Sep 11, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Description
Rendering a nested Mermaid state diagram in the V2 TUI causes immediate, unbounded memory allocation. The reporter experienced an OOM and whole-machine crash. Expected: render the diagram or reject it without exhausting memory.
Confirmed cause: addSideParallelTransition() in packages/merman/src/state/routing.ts:790 assumes its vertical segment has nonzero length:
for (let y = startY + verticalStep; y !== endY; y += verticalStep)
addCell(builder, { x: railX, y, char: "│" })
When startY === endY, verticalStep is 1: the loop starts past its endpoint and increments forever, appending both cells and path points. Vertical routing selects this side route for states in different composite parents, even when they share a row.
The original diagram reaches this through automatic LR → TB reflow at layoutMaxWidth: 120; its failing route has startY = endY = 13. The canvas-size check runs after route construction, and the route-search budget does not cover this loop.
Reproduced deterministically from checkout 770420bd using the actual parser/layout/drawing code bundled for Node 24.11.1. Isolated subprocesses used a 96 MiB old-space cap, 4 MiB semi-space cap, CPU/wall-time limits, and disabled core dumps:
| Input | Current code | Experimental bounded-loop correction |
|---|---|---|
| Original diagram | OOM in 0.45 s; peak RSS 163 MiB | Completes in 0.71 s; peak RSS 102 MiB |
| Reduced fixture below | OOM in 0.24 s; peak RSS 164 MiB | Completes in 0.13 s; peak RSS 71 MiB |
Error: Allocation failed - JavaScript heap out of memory.
The experimental correction used a directional inequality to avoid walking a zero-length segment. It confirms the allocation cause; production route geometry and regression coverage still need fixing. Suggested regression seam: drawStateDiagramGrid, including automatic reflow.
Plugins
Reported diagnostic snapshot: boxedcode-pro 0.16.1, opencode-gemini, opencode-trajectory, opencode-ultra, and one project-local plugin (private name/path omitted). Plugin activation was not independently rechecked. The isolated reproduction directly invokes Merman and does not load these plugins.
OpenCode version
opencode2 v0.0.0-beta-19425 (verified with opencode2 --version; beta channel). The separate opencode executable reports 1.18.30.
Steps to reproduce
- Use a memory-limited isolated subprocess rather than the live TUI: the current code allocates indefinitely.
- Parse this source with
parseMermaidStateDiagramfrompackages/merman/src/state/parser.ts:
stateDiagram-v2
direction TB
A --> B
A --> C
state D {
state E {
B --> F
}
B --> C
}
- Pass the result to
drawStateDiagramGrid(parsed, { layoutMaxWidth: 120 })frompackages/merman/src/state/drawing.ts. - Observe OOM while constructing the
B → Cside route (startY = endY = 8). The TUI reaches this same drawing entrypoint when rendering amermaidfence.
Workaround: use a text fence to bypass Mermaid rendering.
Screenshot and/or share link
None collected; diagnosis used isolated renderer subprocesses.
Operating System
Darwin 25.6.0 arm64 (verified with uname -srm).
Terminal
Ghostty; reported snapshot: TERM=xterm-ghostty, COLORTERM=truecolor. Shell: /bin/zsh. Terminal and shell values are from the supplied snapshot, not independently rechecked.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.