Repository navigation
Description
Summary
With tabs.scope: "global", the tab strip is persisted in a single per-machine file that every TUI client on that machine reads and writes — including clients started with --server <url>, which are connected to a different server. The persisted entries are bare sessionIDs that are only valid on the server that created them, so a client restores tabs it cannot resolve and fails with SessionNotFound when one is selected. There is no way to keep global tabs per server: tabs.scope only accepts cwd or global.
Environment
- opencode version: 2.0.19 (both the local client and the remote server report 2.0.19)
- OS: macOS 27.0.1 (Darwin Kernel Version 27.0.0, arm64) on the machine running the TUI clients; remote server is a Linux container
- Terminal:
TERM_PROGRAM=Apple_Terminal,TERM=xterm-256color,COLORTERM=truecolor - Shell: /bin/zsh
- Install/channel: curl-installed release build, channel
latest. (The state directory also containsbeta/andnext/stores, so other channels have been used previously, but this report is againstlatest2.0.19.) - Active plugins: none found (
opencode plugin list→ "No plugins found"; nopluginkey in~/.config/opencode/opencode.jsonc; no local plugin files)
Reproduction
Two servers are reachable, both at the same client version. A local background service on http://127.0.0.1:49374 and a remote one at http://<machine>.<tailnet>.ts.net:8080. Both are started with the same shared CLI config, no per-invocation overrides:
// ~/.config/opencode/cli.json
{
"tabs": {
"mode": "on",
"scope": "global",
"layout": "horizontal",
"indicators": "status"
}
}-
Start a TUI against the remote server:
opencode --server http://<machine>.<tailnet>.ts.net:8080, create two or three sessions, and quit. -
Observe that the client wrote them into the local machine's global tab store,
~/.local/state/opencode/latest/tui/tabs.json— even though those sessions live on the remote server:{ "global": { "tabs": [ { "sessionID": "ses_f1307fc2…", "title": "WebHarness" }, { "sessionID": "ses_f13c841…", "title": "Open Accredidation" }, { "sessionID": "ses_f13cb85…", "title": "LuminaRust" } ], "unread": {} }, "cwd": {} } -
Start a TUI against the local server:
opencode. -
The three remote sessions are rendered as tabs in this window too.
-
Select one. It fails with a session-not-found error.
The failure is fully deterministic at the API level — all three stored IDs exist on the remote server and all three are 404 on the local one:
$ opencode api get /api/session/ses_f1307fc2…
HTTP 404 Not Found
{"_tag":"SessionNotFoundError","sessionID":"ses_f1307fc2…","message":"Session not found: ses_f1307fc2…"}
$ opencode api get /api/session/ses_f1307fc2… --server http://<machine>.<tailnet>.ts.net:8080
{"data":{"id":"ses_f1307fc2…","title":"WebHarness","location":{"directory":"/projects"}, …}}| sessionID | local server | remote server |
|---|---|---|
ses_f1307fc2… |
404 NotFound | present |
ses_f13c841… |
404 NotFound | present |
ses_f13cb85… |
404 NotFound | present |
Expected Behavior
A TUI client should only restore tabs whose sessions its own server can resolve. Sessions from a different server are invisible to it and should not appear.
Actual Behavior
tabs.scope: "global" is a single global store per machine, and --server is not considered when reading or writing it. Remote sessions leak into a local TUI's tab strip and are unresolvable there.
tabs.scope offers no per-server scope, so the two useful workarounds both have a cost:
tabs.scope: "cwd"separates tab strips by working directory, which happens to work here only because the local and remote path namespaces cannot collide (/Users/…vs./projects/…). That is incidental, not a guarantee — a client that shares a directory string with a foreign server would hit the same failure.OPENCODE_CLI_CONFIG_CONTENT='{"tabs":{"mode":"off"}}'per invocation turns tabs off for one client, at the cost of losing the feature there.
Additional Context
- The tab store is a single file with two buckets,
globalandcwd(~/.local/state/opencode/latest/tui/tabs.json). Thecwdbucket was empty throughout, so this is purely a consequence ofscope: "global". - Session IDs are not namespaced by server, so the store needs to be — by endpoint URL, or by something stable derived from it. There is also no server version/identity recorded alongside the entries.
- Running two clients at the same time works otherwise: the client holds no single-instance lock, and each keeps its own sessions, config, permissions and tools. Only the global tab store collides.
- Both servers report the same version (2.0.19), so this is not a version-skew artifact.
- No matching entry was found in
~/.local/share/opencode/log/opencode.logfor the error underrole=cli; the failure surfaces in the TUI client without a corresponding client-side log line, which also made this harder to diagnose than expected. - Related: the
cwdscope has the same underlying problem, it is just keyed on a value that happens to differ between the two servers here.
Activity
github-actions commentedon Sep 29, 2026
This issue might be a duplicate of an existing issue. Please check:
neriousy commentedon Oct 20, 2026
Thanks for the report. We're tracking this same sharing of saved TUI tabs between different servers in #47998, so I'm closing this as a duplicate. Please follow that issue for updates.