Repository navigation
Description
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
-
On V1, create sessions in a directory that is not inside a git repository, e.g.
~/src. V1 records these withprojectID=globaland apathrelative to/:message=created projectID=global directory=/Users/<user>/src path=Users/<user>/src version=1.17.3 -
Upgrade to V2.
-
cd ~/src && opencode session list -
Open the TUI in the same directory and use
/sessions(orCtrl+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 timeThe 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.pathholds 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_idis not null) are bound to the same project as their parent, and are not listed bysession listregardless. - Numbers here are from a large real database: 373 sessions / 27,959 messages, 316 of them in the orphaned
globalproject.
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
opencode-agent commentedon Oct 12, 2026
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
globalproject 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:
globalhidden after the upgrade), confirmed by several users up to 2.0.27pathagainstglobalglobalsessions are never re-assigned to a project after migration; the open fix PR fix(core): attribute migrated V1 sessions to the project of their directory #51935 links to this oneThe open PR #51935 ("attribute migrated V1 sessions to the project of their directory") addresses this. Your note that the stale
pathhas 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.
rodbarnard commentedon Oct 12, 2026
Thanks — confirmed duplicate. The root cause matches #49827 exactly (V1
globalsessions, 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 = NULLis not equivalent to what V2 writes for a session sitting at its project root. The listing filters onprojectandsubpath, andsubpath=''matchespath = ''but does not matchpath IS NULL:The single row returned in the second case is the one session that already had
path = ''. So the repair has to beSET path = ''. Clearing it toNULLre-owns the session to the correct project but still leaves it filtered out — which is a confusing intermediate state, sinceopencode session listreports the sessions correctly while the TUI shows none.opencode session listomitssubpath, 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 assertingpath: ""for a root session covers it. ButattributeDirectoriesselectsWHERE project_id = 'global', so any install where a user already hand-repaired rows offglobal(for example by following the workaround in this report) falls outside the repair path and stays hidden unless they also correctpaththemselves. That edge may be worth a line wherever the workaround is documented.Everything else in the report stands: 373 sessions intact, 316 stranded under
globalacross two directories (225 + 87), sessions in git repositories unaffected.Two smaller details that may be useful if anyone else is digging into this:
pathcolumn 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.opencode-agent commentedon Oct 12, 2026
Thanks for confirming, and for the careful follow-up!
The correction is useful: if you repair the database by hand,
pathhas to be set to'', notNULL. Otherwise the TUI listing, which also filters onsubpath, still hides the sessions, even thoughopencode session listshows 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?
path = ''correction to the workaround.globalby hand fall outsideattributeDirectories(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.
opencode-agent commentedon Oct 13, 2026
Closing this as a duplicate of #49827, which you confirmed above. Thanks again for the detailed report and the corrected workaround (
path = '', notNULL). Please follow #49827 and PR #51935 for updates. If you haven't yet, adding your workaround correction and the note about rows already moved offglobalthere will help others who hit this.