Skip to content

tui: global tabs are shared across clients with different --server targets, causing SessionNotFound #52080

Closed as duplicate

Description

@reisi007

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 contains beta/ and next/ stores, so other channels have been used previously, but this report is against latest 2.0.19.)
  • Active plugins: none found (opencode plugin list → "No plugins found"; no plugin key 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"
  }
}
  1. Start a TUI against the remote server: opencode --server http://<machine>.<tailnet>.ts.net:8080, create two or three sessions, and quit.

  2. 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": {}
    }
  3. Start a TUI against the local server: opencode.

  4. The three remote sessions are rendered as tabs in this window too.

  5. 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, global and cwd (~/.local/state/opencode/latest/tui/tabs.json). The cwd bucket was empty throughout, so this is purely a consequence of scope: "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.log for the error under role=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 cwd scope has the same underlying problem, it is just keyed on a value that happens to differ between the two servers here.

Activity

  1. github-actions commented on Sep 29, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of an existing issue. Please check:

  2. neriousy commented on Oct 20, 2026

    @neriousy
    Member

    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.

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions