Skip to content

tui: plugins no longer load after cli.json config migration (nightly next channel) #42051

Closed as not planned

Description

@samarth-na

Summary

TUI plugins stopped loading in the next nightly channel after builds that migrated TUI config into cli.json. Plugin entries in tui.json (string/tuple form) and cli.json ({package, options} form) are both ignored — the TUI runtime never attempts to load them. Adding the same plugin to the server config (opencode.jsonc plugins) makes the server load it as a server plugin and crash with TypeError: undefined is not an object (evaluating 'context.storage.memory') because TUI-only context capabilities do not exist server-side.

Environment

  • opencode version: 0.0.0-next-17297 (also reproduced on 0.0.0-next-17296)
  • OS: Linux Fedora 43, 7.1.7-100.fc43.x86_64
  • Terminal: tmux (TERM=xterm-256color, truecolor)
  • Shell: fish
  • Install/channel: next (npm global install of @opencode-ai/cli)
  • Active plugins: local TUI plugin /home/samarth/repos/live-token-info/src/index.tsx (id: live-token-meter, uses context.storage.memory and context.ui.slot("prompt.footer.end")); previously also @renjfk/opencode-voice

Reproduction

  1. Create a local TUI plugin module: index.tsx with /** @jsxImportSource @opentui/solid */, import { Plugin } from "@opencode-ai/plugin/tui", and Plugin.define({ id, setup(ctx) { ctx.storage.memory(...); ctx.ui.slot("prompt.footer.end", ...) } }).

  2. Add it to ~/.config/opencode/tui.json as a tuple:

    "plugin": [
        ["/path/to/plugin.tsx", { "charsPerToken": 4, "showTotals": true }]
    ]

    (Same form used by the working .opencode/tui.json example for the tui-smoke fixture in this repo.)

  3. Restart the TUI (opencode2).

  4. The plugin does not load. No entry appears in ~/.local/state/opencode/plugin-meta.json and no client-side log messages are emitted.

  5. Alternative: add the same plugin to ~/.config/opencode/opencode.jsonc as "plugins": [{ "package": "/path/to/plugin.tsx", "options": {...} }] and restart. The server log shows:

    msg="loading plugin" id=/path/to/plugin.tsx entrypoint="/path/to/plugin.tsx?mtime=..."
    message="failed to load plugin" plugin.id=live-token-meter cause="Cause([Die(TypeError: undefined is not an object (evaluating 'context.storage.memory'))])"
    

Expected Behavior

TUI-kind plugins load in the TUI client with the TUI plugin context (storage, ui, theme, data), either from client config entries (tui.json tuples / cli.json {package, options}) or through server-side kind detection that routes them to the client without executing setup with a server context.

Actual Behavior

  • The TUI runtime ignores all client-config plugin entries (no attempts recorded in plugin-meta.json or logs) since the cli.json migration appeared in next-17098 (server log: migrated cli config on 2026-08-10).
  • Server-config entries load the module as a server plugin and crash in setup on context.storage.memory being undefined.
  • Timeline confirmation: TUI plugin @renjfk/opencode-voice loaded successfully 46 times until the next-17055 era (last load 2026-08-09) and never again after the migration landed; the tui-smoke.tsx fixture loaded once on 2026-07-02 and has not re-loaded since.

Additional Context

Config used for reproduction:

// ~/.config/opencode/tui.json
"plugin": [
    ["/home/samarth/repos/live-token-info/src/index.tsx", { "charsPerToken": 4, "showTotals": true }]
]

// ~/.config/opencode/cli.json (rewritten each start by the built-in migration)
"plugins": [
    { "package": "/home/samarth/repos/live-token-info/src/index.tsx", "options": { "charsPerToken": 4, "showTotals": true } }
]
  • Stable channel 1.18.16 still contains the legacy client-side flow (loading tui config / skipping invalid tui config message strings) and does not exhibit the regression. The nightly binary (next-17297) no longer contains those strings, which matches the client runtime no longer reading client config.
  • Consistently reproducible across roughly 10 client restarts.
  • No workaround found; the server-config entry must be removed to stop the crash on every start. The plugin itself is unaffected (module imports fine; the failure happens inside setup with a server context).

Activity

  1. rorshopping commented on Aug 12, 2026

    @rorshopping

    Tried to reproduce this on current dev (dab2637). The primary symptom — TUI plugins not loading at all — doesn't reproduce there: a plugin declared only in tui.json (e.g. { id, tui }) still loads and activates in the TUI runtime.

    The crash does reproduce, and the mechanism matches what you found: a TUI-only plugin is processed by both runtimes when it's listed in both configs. The server runtime (PluginLoader.loadExternal with kind: "server") imports it independently and rejects it — readV1Plugin throws TypeError: Plugin <spec> must default export an object with server() — which is where the nightly's context.storage.memory error comes from (TUI-only capabilities missing server-side). The TUI runtime loads the same spec fine. Removing the plugin from the server config stops the server-side error and the TUI plugin still works.

    I've put a 4-case reproduction test at packages/opencode/test/plugin/issue-42051.test.ts (server-reject, tui-load, removal-fixes, double-processing). Happy to open a PR if a server-side guard against TUI-only plugins would be useful — e.g. treating a TUI-only module as a no-op (or a clearer warning) in the server runtime instead of a failed to load plugin log.

  2. github-actions commented on Oct 13, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions