Environment
| Item |
Value |
| opencode |
1.18.10 (linux x64, bun runtime) |
| opencode-cache-hit |
0.6.3 (npm @latest) |
| @opencode-ai/plugin SDK |
1.18.10 |
| @opentui/solid |
0.4.5 |
| OS |
linux x64 |
| TUI config |
~/.config/opencode/tui.jsonc → "plugin": ["opencode-cache-hit@latest", ...] |
Summary
On opencode 1.18.10, the cache-hit sidebar panel never appears. tui() is invoked, api.slots.register({ order: 56, slots: { sidebar_content } }) runs successfully, but sidebar_content throws during first render and opencode's per-slot <ErrorBoundary> (v1.17.0+, see README "Update" note) silently unmounts the slot — no crash screen, no stderr, no opencode.log entry. Only an in-process try/catch around the render call surfaces the error:
sidebar_content RENDER ERROR: undefined is not an object (evaluating 'props.trend.color')
TypeError: undefined is not an object (evaluating 'props.trend.color')
at TuiHitRow (dist/index.js:470)
at MainSessionView (dist/index.js:1647)
at CacheHitSidebar (dist/index.js:2087)
...
Root cause
src/tui-panel/components.tsx:172 — TuiHitRow:
export function TuiHitRow(props: {
label: string
bar: string
pct: string
barColor: string
textColor: string
trend?: { text: string; color: string } // optional
}) {
return (
<text>
...
<Show when={props.trend}>
<span style={{ fg: props.trend!.color }}> {props.trend!.text}</span> // ← line 186
</Show>
</text>
)
}
src/main-session-view.tsx:44-46 passes trend conditionally:
trend={
m.perCall().hasTrend ? { text: m.trendLabel(), color: m.trendFg() } : undefined
}
When m.perCall().hasTrend is false, trend === undefined. The <Show when={props.trend}> is supposed to guard, but this relies on SolidJS's lazy children evaluation produced by babel-preset-solid. The published package ships raw .tsx sources (exports["./tui"]: "./index.tsx"), and opencode loads them with bun's generic JSX transform (jsxDEV), which eagerly evaluates the children object — so props.trend!.color runs before Show's when guard, throwing on undefined.
This is structurally the same class of bug README.md warns about (issue #3 — undefined is not an object (evaluating 'config.providers')), just at a different site.
Reproduction
- opencode 1.18.10 +
tui.jsonc: "plugin": ["opencode-cache-hit@latest"]
opencode -s <any-existing-session> (session with prior assistant turns so metrics exist)
- Sidebar shows other plugins' slots but cache-hit panel is absent
- Wrap the
sidebar_content render in try/catch writing to a file → captures the props.trend.color TypeError above
Evidence that the slot is registered but unmounted
Injecting appendFileSync probes into the compiled entry:
[03:07:58.047Z] dist/index.js module evaluated, jsxDEV=function
[03:07:58.060Z] tui() CALLED, api.slots=function ← register succeeded
[03:11:03.897Z] sidebar_content CALLED, session_id=ses_... ← render invoked
[03:11:03.902Z] sidebar_content RENDER ERROR: undefined is not an object (evaluating 'props.trend.color')
Note: opencode routes console.error to the TTY, not fd2, so 2>file captures nothing — the ErrorBoundary fully swallows the throw.
Suggested fixes (any one suffices)
- Defensive access in
components.tsx:186:
<Show when={props.trend}>
<span style={{ fg: props.trend?.color }}> {props.trend?.text}</span>
</Show>
- Ship a compiled bundle built with
babel-preset-solid (lazy children) instead of raw TSX, and point exports["./tui"] at ./dist/tui.js — matching opencode-quota / oh-my-openagent layout. Also add "main" and consider "oc-plugin": ["tui"].
- Skip the
! assertion and rely on the Show guard with a SolidJS-aware build.
Workaround (verified)
Compile locally with bun build then patch the eager-evaluation site:
cd ~/.cache/opencode/packages/opencode-cache-hit@latest/node_modules/opencode-cache-hit
bun build ./index.tsx --outdir ./dist --target node --format esm \
--external solid-js --external @opencode-ai/plugin --external @opencode-ai/sdk \
--external @opentui/core --external @opentui/solid
# then edit dist/index.js: props.trend!.color → props.trend?.color (2 sites)
# and add to package.json: "main":"./dist/index.js",
# "exports": { ".": "./dist/index.js", "./tui": "./dist/index.js", "./tui-panel": "./src/tui-panel/index.ts" }
After this, the panel renders correctly.
Additional notes
@latest is pinned to first-resolved version and never re-pulled (README "Update" section). Survivors of an older broken cache see this even after upstream fixes.
- opencode's slot resolver uses
j.get(name)?.at(-1)?.render (last-registered wins for same-name slots). If opencode-cache-hit and opencode-quota both register sidebar_content, only the last-registered renders. Order plugins in tui.jsonc accordingly when both ship a sidebar panel.
Environment
1.18.10(linux x64, bun runtime)0.6.3(npm@latest)1.18.100.4.5~/.config/opencode/tui.jsonc→"plugin": ["opencode-cache-hit@latest", ...]Summary
On opencode 1.18.10, the cache-hit sidebar panel never appears.
tui()is invoked,api.slots.register({ order: 56, slots: { sidebar_content } })runs successfully, butsidebar_contentthrows during first render and opencode's per-slot<ErrorBoundary>(v1.17.0+, see README "Update" note) silently unmounts the slot — no crash screen, no stderr, noopencode.logentry. Only an in-process try/catch around the render call surfaces the error:Root cause
src/tui-panel/components.tsx:172—TuiHitRow:src/main-session-view.tsx:44-46passestrendconditionally:When
m.perCall().hasTrendisfalse,trend === undefined. The<Show when={props.trend}>is supposed to guard, but this relies on SolidJS's lazy children evaluation produced bybabel-preset-solid. The published package ships raw.tsxsources (exports["./tui"]: "./index.tsx"), and opencode loads them with bun's generic JSX transform (jsxDEV), which eagerly evaluates thechildrenobject — soprops.trend!.colorruns beforeShow'swhenguard, throwing onundefined.This is structurally the same class of bug README.md warns about (issue #3 —
undefined is not an object (evaluating 'config.providers')), just at a different site.Reproduction
tui.jsonc:"plugin": ["opencode-cache-hit@latest"]opencode -s <any-existing-session>(session with prior assistant turns so metrics exist)sidebar_contentrender in try/catch writing to a file → captures theprops.trend.colorTypeError aboveEvidence that the slot is registered but unmounted
Injecting
appendFileSyncprobes into the compiled entry:Note: opencode routes
console.errorto the TTY, not fd2, so2>filecaptures nothing — the ErrorBoundary fully swallows the throw.Suggested fixes (any one suffices)
components.tsx:186:babel-preset-solid(lazy children) instead of raw TSX, and pointexports["./tui"]at./dist/tui.js— matching opencode-quota / oh-my-openagent layout. Also add"main"and consider"oc-plugin": ["tui"].!assertion and rely on theShowguard with a SolidJS-aware build.Workaround (verified)
Compile locally with
bun buildthen patch the eager-evaluation site:After this, the panel renders correctly.
Additional notes
@latestis pinned to first-resolved version and never re-pulled (README "Update" section). Survivors of an older broken cache see this even after upstream fixes.j.get(name)?.at(-1)?.render(last-registered wins for same-name slots). Ifopencode-cache-hitandopencode-quotaboth registersidebar_content, only the last-registered renders. Order plugins intui.jsoncaccordingly when both ship a sidebar panel.