Skip to content

sessions: non-git directories orphan all pre-upgrade sessions after V1→V2 upgrade #54815

Closed as duplicate

Description

@rodbarnard

Summary

After upgrading from V1 (1.18.30) to V2 (2.0.25), every session previously created in a directory that is not inside a git repository disappears from the TUI and from opencode session list. The sessions are still present and intact in the database; they are orphaned because V2 assigns such a directory a brand-new project instead of reusing the shared global project that V1 used.

This is V1 data becoming unreachable without any error, so it reads as data loss until you inspect the DB.

Environment

  • opencode version: 2.0.25 (upgraded from 1.18.30_2)
  • OS: Darwin 27.0.0, arm64
  • Terminal: Apple_Terminal, TERM=xterm-256color, COLORTERM=truecolor
  • Shell: /bin/zsh
  • Install/channel: homebrew (brew upgrade opencode), latest channel
  • Active plugins: opencode-see-image@1.4.0

Reproduction

  1. On V1, create sessions in a directory that is not inside a git repository, e.g. ~/src. V1 records these with projectID=global and a path relative to /:

    message=created projectID=global directory=/Users/<user>/src path=Users/<user>/src version=1.17.3
    
  2. Upgrade to V2.

  3. cd ~/src && opencode session list

  4. Open the TUI in the same directory and use /sessions (or Ctrl+O, which shows "recent sessions and projects together").

Expected Behavior

Sessions created before the upgrade stay visible in the directory they were created in. The migration guide states V2 has exactly three intentional breaking changes (plugin API, server API, cli.json) and that other supported V1 functionality is intended to remain compatible, so project resolution for non-git directories should not regress.

Actual Behavior

opencode session list in ~/src returned 1 session, while the database held 225 sessions with directory=/Users/<user>/src.

V1 assigned every session outside a git repo to one shared global project (worktree=/), storing the directory as a path. V2 instead created a new project for ~/src at upgrade time, so those 225 sessions no longer belong to the project the directory now resolves to:

select project_id, count(*) from session_v2
 where directory = '/Users/<user>/src' group by project_id;

-- global                                     225
-- 5137767e095037c6c9e3f72abd56e101fa2a3e0e     1   <- created at upgrade time

The distribution was identical in the pre-upgrade session table, so the migration itself copied the rows correctly — the breakage is in project resolution, not in data migration.

V2 appears to create a project row on demand for any non-git directory it is launched in. Launching a single command in two further directories created two more project rows:

Directory global sessions Project after V2
~/src 225 5137767e… (new, created at upgrade time)
~/ 87 b10b62cb… (new)
/ 0 e22c0bef… (new)

The global project row still exists, but no directory resolves to it, so the 316 sessions bound to it became unreachable. Sessions in git repositories are unaffected — they kept their project IDs and still list correctly.

A side effect of the same behaviour: ~60 stray project rows were created for arbitrary non-git directories during a single session that ran commands in various directories (e.g. ~/src/assets, ~/src/config, ~/src/layout, ~/src/sections, ~/src/snippets, ~/src/templates).

Additional Context

The regression was observable during the upgrade and was misread as a false alarm. Running the V2 binary at upgrade time, before relaunching the TUI, produced:

session list: EMPTY ... 1.3GB DB of sessions but V2 `session list` shows nothing.

This was then attributed to "~/src itself isn't a project with its own sessions", after other directories happened to list rows — those directories only listed because they are git projects. The post-migration verification consequently reported sessions as verified on the wrong evidence. Worth noting because it makes this a silent regression rather than something a user would catch from a visible error.

Notes on the data:

  • session_v2.path holds the V1 relative path (Users/<user>/src) and must be cleared when a session's directory becomes its own project root, otherwise it is still filtered out.
  • Subagent sessions (parent_id is not null) are bound to the same project as their parent, and are not listed by session list regardless.
  • Numbers here are from a large real database: 373 sessions / 27,959 messages, 316 of them in the orphaned global project.

Workaround that restored access (applied directly to the database, since no TUI-side control exists for this):

UPDATE session_v2
   SET project_id = '<project id whose canonical path is the session directory>',
       path = NULL
 WHERE project_id = 'global' AND directory = '<session directory>';

After this, opencode session list in ~/src went from 1 to 112 root sessions, and in ~/ from 0 to 39. This is a data repair the user should not have to perform; ideally V2 would fall back to the existing global project for directories without a git root, or migrate global sessions to the project their directory resolves to.

Activity

  1. opencode-agent commented on Oct 12, 2026

    @opencode-agent
    Contributor

    Thanks for the very thorough write-up, including the DB numbers and the SQL workaround! I think this is a duplicate of #49827: V1 sessions from non-git directories stay under the global project after the V1→V2 migration. V2 gives each such directory its own project, so the sessions are still in the database but no longer show up in that directory's session list.

    Possible duplicates and closely related issues, ordered from most to least likely:

    The open PR #51935 ("attribute migrated V1 sessions to the project of their directory") addresses this. Your note that the stale path has to be cleared too may be worth adding on #49827 or that PR.

    The bot will close this issue as a duplicate in 1 day unless you reply explaining how your problem is different. Please follow #49827 for updates.

  2. rodbarnard commented on Oct 12, 2026

    @rodbarnard
    Author

    Thanks — confirmed duplicate. The root cause matches #49827 exactly (V1 global sessions, V2 per-directory project, sessions still in the DB but filtered out of the project-scoped listing), so closing this is the right call.

    One correction to my own report, and one thing worth recording for the fix.

    Correction: the workaround in the body is incomplete. Setting path = NULL is not equivalent to what V2 writes for a session sitting at its project root. The listing filters on project and subpath, and subpath='' matches path = '' but does not match path IS NULL:

    GET /api/session?limit=500&parentID=null&project=<p>            -> 112
    GET /api/session?limit=500&parentID=null&project=<p>&subpath=   -> 1
    

    The single row returned in the second case is the one session that already had path = ''. So the repair has to be SET path = ''. Clearing it to NULL re-owns the session to the correct project but still leaves it filtered out — which is a confusing intermediate state, since opencode session list reports the sessions correctly while the TUI shows none. opencode session list omits subpath, so it does not reproduce the TUI's filter; worth knowing when verifying a repair.

    For #51935: the fix itself is correct — subpath: target === root ? "" : target.slice(root.length + 1) produces exactly the value the filter expects, and the test asserting path: "" for a root session covers it. But attributeDirectories selects WHERE project_id = 'global', so any install where a user already hand-repaired rows off global (for example by following the workaround in this report) falls outside the repair path and stays hidden unless they also correct path themselves. That edge may be worth a line wherever the workaround is documented.

    Everything else in the report stands: 373 sessions intact, 316 stranded under global across two directories (225 + 87), sessions in git repositories unaffected.

    Two smaller details that may be useful if anyone else is digging into this:

    • The path column holds the directory relative to the owning project, so when a session's directory becomes its own project root the correct value is the empty string, not the old V1 relative path.
    • V2 creates a project row on demand for any non-git directory it is launched in. Running a single command in two more directories while investigating created two more project rows, and a session that ran commands across several directories left behind ~60 stray project rows. Same underlying behaviour, different symptom.
  3. opencode-agent commented on Oct 12, 2026

    @opencode-agent
    Contributor

    Thanks for confirming, and for the careful follow-up!

    The correction is useful: if you repair the database by hand, path has to be set to '', not NULL. Otherwise the TUI listing, which also filters on subpath, still hides the sessions, even though opencode session list shows them. So far that detail is only written down here.

    This issue will be closed as a duplicate, so the note will be easy to miss here. Could you post both points on #49827, where people with this problem are following along, and on PR #51935, which is open?

    1. The path = '' correction to the workaround.
    2. Rows already moved off global by hand fall outside attributeDirectories (WHERE project_id = 'global') and stay hidden.

    The stray project rows created for every non-git directory are a separate symptom. If they bother you, they could be reported in their own issue.

    This issue will still be closed as a duplicate of #49827 as planned.

  4. opencode-agent commented on Oct 13, 2026

    @opencode-agent
    Contributor

    Closing this as a duplicate of #49827, which you confirmed above. Thanks again for the detailed report and the corrected workaround (path = '', not NULL). Please follow #49827 and PR #51935 for updates. If you haven't yet, adding your workaround correction and the note about rows already moved off global there will help others who hit this.

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