Skip to content

desktop: sessions from unrelated folders merge into a shared "global" project #55133

Open

Description

@wangminle

Summary

In the desktop app, sessions from unrelated folders are merged into one shared project when those folders do not have an independently resolvable project identity (they are not a git repo, or are a git repo with no commits yet / unborn HEAD). Selecting one folder in the sidebar shows sessions created in other folders too.

Environment

  • opencode version: 2.0.26
  • OS: Darwin 27.2.0 (darwin arm64)
  • Terminal: Unavailable: terminal environment variables are not set (observed in the desktop app)
  • Shell: /bin/zsh
  • Install/channel: latest
  • Active plugins: None found in config

Reproduction

  1. Create two or more separate folders that are either (a) not a git repository, or (b) a git repository created with git init but with no commits (unborn HEAD).
  2. Open each folder as its own project in the desktop app and start/run a session in each.
  3. In the sidebar, select one of those folders and look at its session list.

Expected Behavior

The session list for a selected project/folder should only contain sessions that belong to that folder.

Actual Behavior

The session list contains sessions from all folders that lack an independent project identity, i.e. sessions from unrelated folders are mixed into the selected one. It is intermittent in practice only because most folders are real git repositories with commits (those get their own stable project id and stay isolated); the problem appears whenever such "identity-less" folders are opened together.

Additional Context

Root cause appears to be that OpenCode cannot derive a project id for a folder when it is not a VCS repo, or is a repo with an unborn HEAD, and falls back to a single shared catch-all project (project_id = "global"). Because the session list is filtered by project_id (with no fallback to directory), every folder that maps to global shares one session pool.

Evidence from local storage at ~/.local/share/opencode/opencode.db (names redacted):

1. One global bucket holds far more sessions than any real project, spanning many distinct directories

session_v2 grouped by project_id (top rows):
  global                                 156
  <hash-A>                                38
  <hash-B>                                35
  ...

2. The worktree table maps several different directories to the same project_id = global

project_id | directory
global     | <home>/.../folderA
global     | <home>/.../folderB
global     | <home>/.../folderC

3. Sessions shown under the wrong project all carry project_id = global while their directory differs

title                | project_id | directory
<session-in-folderA> | global     | <home>/.../folderA
<session-in-folderB> | global     | <home>/.../folderB
<session-in-folderC> | global     | <home>/.../folderC

4. Correlation: folders that fall back to global have no commits; folders with their own project id have commits

directory | git toplevel | HEAD commits | resulting project_id
folderA   | yes          | unborn/fatal | global
folderB   | yes          | unborn/fatal | global
folderC   | no           | n/a          | global
repoX     | yes          | 30           | <unique hash>
repoY     | yes          | 11           | <unique hash>

Workaround

Give each affected folder a real git repository with at least one commit:

cd <folder> && git add -A && git commit -m "init"

After that OpenCode assigns it a unique project id and its sessions no longer mix. For non-repo folders, git init followed by one commit achieves the same.

Suggested fix

When a folder has no resolvable project identity, either generate a per-directory project id, or fall back to filtering sessions by directory instead of collapsing everything into a single global project.

Activity

  1. opencode-agent commented on Oct 13, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed report. I can reproduce this on 2.0.26, and it still happens on the latest dev (65f8907).

    Steps (the script below does them):

    1. Create two folders and run git init in each, with no commits.
    2. Create a session in each folder.
    3. Run opencode session list in either folder.

    Both sessions show up in both lists, each with project id global.

    One difference from your report: on v2, a plain folder with no repository gets its own project id and stays separate in my test. The plain folders under global in your database were most likely imported from v1, which is tracked in #50551 and #55081. The empty-repo case is still a real problem on v2.

    Related:

    Until this is fixed, your workaround of making one commit in each such repo gives it its own project.

    Reproduction script (repro.sh)
    #!/usr/bin/env bash
    # Reproduces #55133: unrelated git repos with no commits share project_id "global",
    # so `opencode session list` in one folder shows sessions created in the other.
    # Usage: bash repro.sh            (uses `opencode` from PATH)
    #        OC="bun ... packages/cli/src/index.ts" bash repro.sh   (run from source)
    set -euo pipefail
    OC=${OC:-opencode}
    CONN=${CONN:---standalone}   # or CONN="--server http://127.0.0.1:PORT"
    ROOT=$(mktemp -d /tmp/opencode/repro55133.XXXX)
    export HOME=$ROOT/home XDG_DATA_HOME=$ROOT/home/.local/share XDG_CONFIG_HOME=$ROOT/home/.config XDG_STATE_HOME=$ROOT/home/.local/state XDG_CACHE_HOME=$ROOT/home/.cache
    mkdir -p "$HOME"
    $OC --version
    
    # Two unrelated folders, both `git init` with no commits (unborn HEAD), plus a plain folder.
    mkdir -p "$ROOT/folderA" "$ROOT/folderB" "$ROOT/folderC"
    git -C "$ROOT/folderA" init -q
    git -C "$ROOT/folderB" init -q
    
    for f in folderA folderB folderC; do
      (cd "$ROOT/$f" && $OC api $CONN POST /api/session \
         -d "{\"title\":\"session-in-$f\",\"location\":{\"directory\":\"$ROOT/$f\"}}" >/dev/null)
    done
    
    for f in folderA folderB folderC; do
      echo "=== opencode session list in $f"
      (cd "$ROOT/$f" && $OC session list $CONN --format json | python3 -c 'import json,sys; [print(" ", s["title"], s["projectId"], s["directory"]) for s in json.load(sys.stdin)]')
    done

    Run it: bash repro.sh

  2. Dante-dan commented on Oct 14, 2026

    @Dante-dan
    Contributor

    I’m taking the CLI reproduction described above: session list currently filters only by project ID. I’ll use the resolved directory when the project is global, matching the existing TUI fallback, and preserve project-wide listing for repositories with their own identity. This does not change project identity or the session-move behavior in #50979.

  3. ejvallej commented on Nov 2, 2026

    @ejvallej

    Confirming the unborn-HEAD → project.id = "global" fallback on the current release, and adding one symptom that is arguably worse than the session mixing, plus the id-derivation detail.

    Version differs, same cause

    • opencode 2.2.5 — Desktop, bundled CLI 2.2.5 (@opencode/desktop), Linux 7.2.9-1-MANJARO x86_64, Wayland/KDE, Electron 44.10.0, system install at /usr/lib/opencode-desktop, plugin opencode-mem@latest.
    • A separate standalone CLI 1.18.16 is also on PATH; not involved.

    Additional symptom: Desktop lands on /, not on the chosen folder

    The global project's worktree is /. So a user opening a folder in a zero-commit repo does not merely see other folders' sessions merged — the app opens the root project and shows the whole /. Nothing tells the user why; it looks like the folder was opened wrong. A single affected folder is enough, so this reproduces without setting up a second folder.

    mkdir -p /tmp/repro/project && cd /tmp/repro/project && git init
    git rev-parse HEAD   # fatal: ambiguous argument 'HEAD' -> unborn HEAD
    
    opencode-cli api get "/api/location?location%5Bdirectory%5D=%2Ftmp%2Frepro%2Fproject"
    # {"directory":"/tmp/repro/project",
    #  "project":{"id":"global","directory":"/tmp/repro/project","canonical":"/tmp/repro/project"}}

    No row is inserted into project; the global row keeps worktree = / and vcs = git, and every location boot then logs:

    INFO  message="location services booted" directory="/home/<user>/projects/<folder>/2026-11-02 - Cotización"
    INFO  message="location services booted" directory=/
    WARN  message="worktree discovery failed" directory=/ strategy=git error=Worktree.DirectoryUnavailableError
    

    That worktree discovery failed warning repeats for directory=/ on every boot, because global claims vcs = git at the filesystem root. Worth a separate look: a catch-all project probably should not carry vcs = git at /.

    Control cases (2.2.5, same service)

    Directory .git Commits project.id
    git init only yes 0 global
    git init + one commit yes 1 own id
    plain directory (no .git) no — own id
    subdirectory of a zero-commit repo yes (parent) 0 global, canonical = repo root

    Note the asymmetry: a plain directory does get its own id, so it is specifically the unborn-HEAD branch of the VCS path that falls through to the global project.

    Project id appears to be derived from the initial commit (possible collision)

    While fixing my folders I hit this: two independent repos whose initial commits were identical — same empty tree, same author/date/message, trivially produced by running git commit --allow-empty -m "init" twice within the same second in two different repos — resolved to the same project id, and only one row existed in project. Amending one commit to a distinct message gave it a distinct id.

    So project identity for git repos seems to come from the initial commit object, with no path component. That means two unrelated repositories with an identical first commit can collide and share sessions even though both have commits. Including the canonical path in the identity would remove that.

    Workaround that works

    Give the repo one commit, or drop the unused .git, then reload:

    git -C /tmp/repro/project commit --allow-empty -m init
    opencode-cli api post /api/location/reload
    opencode-cli api get "/api/location?location%5Bdirectory%5D=%2Ftmp%2Frepro%2Fproject"
    # ... "project":{"id":"d4681d559a12aa32af1bdc3c6abf073a5723d9bc", ...}  -> own project, opens correctly

    Suggested fix direction

    Since an unborn HEAD is a normal state for a freshly initialized repo, that branch should still mint a project identity (path-derived) instead of falling through to global; and global should not be a git worktree at /. Failing that, the Desktop should at least refuse to open the folder with an explicit error rather than silently switching the user to /.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions