Repository navigation
Closed as not planned
Closed as not planned
Copy link
Labels
Description
Summary
TUI crashes with Orphan text error: "<string>" must have a <text> as a parent when a TUI plugin slot (sidebar.content) returns a plain string from render(). Plugins are effectively forced to return plain strings because importing @opentui/* inside a TUI plugin fails on next-17403 with OPENTUI_FORCE_WCWIDTH already registered with different configuration (host bug).
Environment
- opencode version: 0.0.0-next-17403 (next channel)
- OS: Linux 5.15.0-1081-oracle fix typo in [www/src/content/docs/docs/config.mdx] #87~20.04.1-Ubuntu SMP Mon Apr 28 12:53:11 UTC 2025 (aarch64 / arm64)
- Terminal: VS Code 1.133.0 (
TERM=xterm-256color, truecolor) - Shell: /bin/bash
- Install/channel: next (via
~/.opencode/bin/opencode2) - Active plugins:
usage-tracker.js(server),usage-tracker-tui.js(local TUI plugin at~/.config/opencode/tui/usage-tracker-tui.js)
Reproduction
- Register a TUI plugin claiming the sidebar slot whose
renderreturns a plain string:
import { Plugin } from "@opencode-ai/plugin/tui";
export default Plugin.define({
id: "plain-string-slot",
setup: (ctx) => {
const claim = ctx.ui.slot({
append: "sidebar.content",
render: () => "Usage\nin 545.3k out 125.8k\nre 129.7k cache 139.8M/0\nhit 100% cost $0.2697\ntotal 140.6M steps 68",
});
return () => claim();
},
});- Open the TUI, navigate to a session view with the sidebar rendered.
- TUI crashes:
Orphan text error: "Usage\nin 545.3k ..." must have a <text> as a parent: box-318 above text-node-554.
Expected Behavior
Either of:
- A plain string returned from a slot
render()is wrapped in a<text>node by the host (so strings are a valid slot payload), or @opentui/*is importable from TUI plugin code so plugins can return proper element trees.
Neither path should crash the TUI. The crash takes down the whole terminal UI, not just the slot.
Actual Behavior
Full TUI crash with the orphan-text error. Stack trace:
Error: Orphan text error: "Usage
in 545.3k out 125.8k
re 129.7k cache 139.8M/0
hit 100% cost $0.2697
total 140.6M steps 68" must have a <text> as a parent: box-318 above text-node-554
at w$ (../../node_modules/.bun/@[email protected]+b9027845aeff97b0/node_modules/@opentui/solid/index.bun.js:559:17)
at N (../../node_modules/.bun/@[email protected]+b9027845aeff97b0/node_modules/@opentui/solid/index.bun.js:442:7)
at P (../../node_modules/.bun/@[email protected]+b9027845aeff97b0/node_modules/@opentui/solid/index.bun.js:311:13)
...
at children (../tui/src/routes/session/sidebar.tsx:66:7)
...
at children (../tui/src/routes/session/index.tsx:1143:22)
Additional Context
- The offending plugin was written to return a plain string only because importing
@opentui/*in a TUI plugin dies withOPENTUI_FORCE_WCWIDTH already registered with different configuration— so onnext-17403there is no working way to render styled TUI content from a plugin slot. - Crash is deterministic: every render of the slot once the plugin has data. Sidebar is unrecoverable while the plugin is active.
- The session being viewed at crash time had 68 steps; the sidebar showed live usage stats (input/output/reasoning/cache/cost).
Activity
mirtalha1 commentedon Aug 14, 2026
Recurred ~25 min after filing, same build (next-17403), same error signature on the no-data path:
So the crash hits both code paths (empty state and populated state) — any render of the slot payload crashes the TUI, not just data-rich renders.
Local workaround for the
OPENTUI_FORCE_WCWIDTH already registeredimport blocker — confirmed working on next-17403: clear the host's env-registry singleton before importing@opentui/solid:With that, slot
render()can return proper element trees built viacreateElement("box")/createElement("text")+setProp/insert(solid universal API re-exported by@opentui/solid), and the orphan-text crash disappears. If the import still fails, returningnullfromrender()is tolerated by the host (renders nothing, no crash).Host-side fixes still warranted regardless:
render()should be wrapped in a<text>node (or rejected with a per-slot error) instead of crashing the whole TUI.@opentui/coresingleton collision shouldn't be reachable from plugin module code — plugins loading a second copy of the bundled deps is a normal resolution outcome.github-actions commentedon Oct 15, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.