Skip to content

sessions: V1 sessions from non-git directories hidden from the project session list #53450

Closed as duplicate

Description

@vc

Summary

After upgrading from OpenCode V1 to V2, sessions that were created in a non-git project directory disappear from the TUI sidebar and from opencode session list when run in that directory. The sessions are still in the database and remain reachable through the global session picker and through a directory-filtered API query, so no data is lost — they are simply never listed for the project.

Environment

  • opencode version: 2.0.23 (npm @opencode/cli, release channel: latest)
  • OS: Microsoft Windows 10 Pro, win32 x64
  • Terminal: TERM=xterm-256color, no TERM_PROGRAM/COLORTERM/WT_SESSION (Cygwin bash)
  • Shell: C:\Windows\system32\cmd.exe as the user shell; opencode shell is set to c:/tools/cygwin/bin/bash.exe
  • Active plugins: opencode-working-memory (loaded from both opencode.json plugins and cli.json plugins)

Reproduction

  1. With V1 (1.18.34), work in a directory that is not a git repository and create several sessions, including subagent sessions. Repeat for a couple of directories.
  2. Install V2 (2.0.23). V2 reports the storage migration as done (storage/migration contains 2; GET /api/experimental/migration/v1 returns {"status":"completed"}).
  3. Start V2 in one of those directories.
  4. Observe the TUI sidebar and opencode session list in that directory.

Expected Behavior

The sidebar and opencode session list in a directory should show the sessions previously created there, just like opencode session list does for git-backed directories.

Actual Behavior

Only sessions created by V2 after the upgrade are listed. In the reproduction directory:

$ opencode session list
ses_ef1c39c0dffeaGbYCNag2CgVHN  Opencode sessions not visible   (V2, current session)

# before the manual repair described below
$ opencode api get "/api/session?project=f47d456faacca9e42cf9043753505a4f46dfc274"
1 session
$ opencode api get "/api/session?directory=D%3A%2Fwork%2Fexample"
6 sessions

The sessions are intact and were found without difficulty in the global session picker, which confirms the loss is a listing/filtering problem rather than a storage problem.

Additional Context

Two independent defects stack up here. Both are required for the symptom, and fixing only one still leaves the sessions hidden.

1. V1 sessions are attributed to the pseudo-project global and are never re-homed

V1 stored sessions of every non-git directory under the special project id global (its project.worktree is the sentinel /). V2 instead creates one project row per directory. The session list is filtered by project_id, so V1-era sessions in non-git directories are unreachable from the project's session list:

-- after the upgrade
SELECT project_id, version, count(*), max(time_updated)
  FROM session_v2 GROUP BY project_id ORDER BY count(*) DESC;

--  global | 1.14.19 .. 1.18.30 | 366 | ...
--  f47d456f... (worktree = D:/work/example) | 2.0.23 | 1 | ...

The same directory holds six sessions, but the project-scoped query returns only the one created by V2:

SELECT id, project_id, version, path FROM session_v2
 WHERE directory = 'D:/work/example';
-- ses_f6d5d693...  global     1.18.30  MyDocs/My Devices/_lan/_OS/vm-iredmail
-- ses_f539f413...  global     1.18.30  (subagent)
-- ses_ef1c39c0...  f47d456f…  2.0.23   (NULL)

migrateFromGlobal() does not repair this: it only runs when the target project row is created for the first time, so by the time V2 is started in an already-populated directory there is nothing to trigger it. The general project-id repair in Project.fromDirectory() matches stale projects by project.worktree, and the global project has worktree = '/', so it never matches a real directory either.

On the affected machine this accounts for 366 sessions across 85 directories (e.g. 71 sessions in one directory, 29 in another), all created between V1.4 and V1.18.

2. path must be ''; NULL is filtered out by the subpath query

After re-pointing project_id at the correct V2 project, the sessions were still missing from the sidebar. The TUI sends subpath= (empty string), and the project-scoped query filters path = '', which does not match NULL:

?project=f47d456f…            -> 6 sessions
?project=f47d456f…&subpath    -> 1 session

The single row that survives the subpath query is the V2 session, whose path is the empty string; the five repaired rows had path IS NULL and were dropped. Setting path = '' made all six appear. This is the path = '' edge case in the project listing path: rows migrated from V1 carry a NULL path (V1 recorded a relative path such as MyDocs/My Devices/_lan/_OS/vm-iredmail, relative to the global worktree /), and no code normalizes them to ''.

Manual repair used as a workaround

Reversible SQL, applied to session_v2 only:

UPDATE session_v2
   SET project_id = '<project id for the directory>',
       path       = ''
 WHERE project_id = 'global'
   AND directory  = 'D:/work/example';

After this, ?project=<id>&subpath returns all six sessions and the TUI sidebar lists them. A full database backup was taken before the change.

Related

This looks like the same root cause family as the already-reported sessions-not-shown issues for non-git directories and for project-id drift (git init, history rewrite, /sessions scope). Per the V1→V2 migration guide, only three breaking changes are intentional (plugins, server API, terminal client config) and "supported V1 functionality outside those areas is intended to remain compatible with V1", so this is filed as a compatibility bug.

Project ids for non-git directories are not a plain hash of the path (sha1/md5/sha256 of the directory and of common normalizations do not match), so they cannot be backfilled offline — Project.fromDirectory() has to generate them.

Activity

  1. github-actions commented on Oct 5, 2026

    @github-actions
    Contributor

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

  2. EnglandLobster commented on Oct 9, 2026

    @EnglandLobster

    Reproduced on macOS 26.5.2 after upgrading from 1.18.35 to 2.0.25, with no global plugins. All 75 legacy sessions were present in session_v2, but 41 sessions across two non-Git directories remained under global. Reassigning their project and setting path = "" restored project-scoped listing without changing transcript rows or timestamps.

    The current v2 branch still has a runtime gap: Project.resolve() creates a directory-specific project, but persist() returns early when project.vcs is absent. Consequently it never publishes Worktree.Event.Resolved, which is what the Session projector uses to adopt legacy sessions.

    I have a regression test that fails on current v2 and a focused fix that announces markerless directory resolution with exact-directory adoption. Exact matching matters here: resolving a home directory must not claim legacy sessions in unrelated child directories. This complements #51935's migration-time repair by handling directories when they are opened, including projects already created by affected releases.

  3. EnglandLobster commented on Oct 15, 2026

    @EnglandLobster

    Reproduced again on 2.0.29 (Ubuntu 24.04, upgrade path 1.18.34 → 2.0.26 → 2.0.29): of 211 migrated/created sessions, 164 remained under project_id=global while their directory columns pointed at real, mostly non-git directories (~, ~/interview_experiences, …). The project-scoped picker listed only the handful that happened to be attributed correctly, which reads as "history gone after upgrade".

    For anyone hitting this before a fix lands: POST /api/session/{id}/move with {"directory": "<the session's own directory>"} is a clean server-side workaround. Verified on 2.0.29 that a move:

    • reassigns project_id to the directory-resolved project (no longer global),
    • normalizes path to '', matching natively-created V2 sessions,
    • leaves transcript rows and timestamps untouched.

    After moving the affected sessions, they reappear in the project-scoped /sessions list. Batch sketch:

    # sessions stuck under global whose directory is a registered project
    sqlite3 -readonly ~/.local/share/opencode/opencode.db \
      "SELECT id, directory FROM session_v2
       WHERE project_id='global' AND directory IN ('/home/me', '/home/me/proj')" \
    | while IFS='|' read -r sid dir; do
        opencode api post "/api/session/$sid/move" --data "{\"directory\":\"$dir\"}" >/dev/null
      done

    Moved 57/57 cleanly here; only global sessions whose directories no longer exist (disposable automation workspaces) were left alone.

    This is runtime repair rather than migration-time, so it also covers projects already mis-attributed by affected releases — the same population the Worktree.Event.Resolved adoption fix targets.

  4. rekram1-node commented on Oct 21, 2026

    @rekram1-node
    Contributor

    Same issue as #49827, which has more activity. The fix for the runtime side is #54048. Tracking there.

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