Summary
Moving a session into a target directory that is the target project's own root can store a bogus session.path, after which the session no longer appears in that project's session list. The move reports success (HTTP 204) with no error.
Environment
- opencode version: 2.0.23 (npm
@opencode/cli)
- OS: Microsoft Windows 10 Pro, win32 x64
- Shell: Cygwin bash; the
git on PATH is Cygwin git (returns POSIX-style paths)
- Plugins: none involved
- Project is a git repository, sessions created on v2.0.23
Steps to reproduce
-
Create a git repository D:/home/<user>/probe and start opencode in it once from a Cygwin shell, so the project row gets worktree / canonical in POSIX form (/home/<user>/probe) while sessions keep Windows-form directories.
-
Create a session somewhere else (POST /api/session with directory=D:/work/a) and note its id.
-
Move it to the project root:
POST /api/session/<sessionID>/move
{"directory":"D:/home/<user>/probe"}
Response: 204 No Content.
-
Ask the server the same question the TUI asks:
GET /api/session?project=<projectID>&subpath
Actual Behavior
GET /api/session?project=<projectID>&subpath returns 0 sessions, and the moved session is not listed by opencode session list for that project, even though GET /api/session/<sessionID> returns it.
The stored row is:
id | ses_...
project_id | <projectID>
directory | D:/home/<user>/probe
path | ../../../home/<user>/probe
Expected Behavior
The target directory is the project's own root, so path must be the empty string, and the session must show up at the project root subpath:
GET /api/session?project=<projectID>&subpath -> 1 session
path -> ''
Why
session.path is meant to hold the session directory relative to the project root ('' means the project root), and the project session list filters on exactly that value via the subpath query parameter. The move handler appears to derive it with something equivalent to path.relative(project.worktree, directory) without normalizing path styles. Here the project root is stored POSIX (/home/<user>/probe) while the requested directory is Windows (D:/home/<user>/probe), so the relative path is computed across two unrelated roots and comes out as ../../../home/<user>/probe.
The same normalization gap was observed once on session creation rather than on move: a session created with a directory that did not resolve to a known project fell back to the global pseudo-project (worktree = '/') and received path = '../../home/<user>' instead of ''.
Two related observations, in case they are of interest to the maintainers:
project.worktree itself stores whatever git discovery returns, so a project discovered from a Cygwin shell keeps a POSIX root while every session directory stays Windows-form. All other projects on the machine are stored Windows-form, so this also means the same physical directory can be represented in two styles.
- The session move endpoint (
POST /api/session/{sessionID}/move) is not mentioned in the public docs, although the /move TUI command uses it.
Impact
Silent loss of visibility: the move succeeds, the session disappears from the sidebar and from opencode session list for the target project, and neither reopening the project nor repeating the move brings it back — the row has to be corrected in the database (path = ''). This is the same failure mode that makes pre-migration sessions invisible.
Suggested fix
Normalize the project root and the target directory to a single form before computing the relative path, and short-circuit to '' when both resolve to the same directory. Possibly also normalize project.worktree to the platform's native form when it is stored.
Summary
Moving a session into a target directory that is the target project's own root can store a bogus
session.path, after which the session no longer appears in that project's session list. The move reports success (HTTP 204) with no error.Environment
@opencode/cli)gitonPATHis Cygwin git (returns POSIX-style paths)Steps to reproduce
Create a git repository
D:/home/<user>/probeand start opencode in it once from a Cygwin shell, so the project row getsworktree/canonicalin POSIX form (/home/<user>/probe) while sessions keep Windows-form directories.Create a session somewhere else (
POST /api/sessionwithdirectory=D:/work/a) and note its id.Move it to the project root:
Response:
204 No Content.Ask the server the same question the TUI asks:
Actual Behavior
GET /api/session?project=<projectID>&subpathreturns 0 sessions, and the moved session is not listed byopencode session listfor that project, even thoughGET /api/session/<sessionID>returns it.The stored row is:
Expected Behavior
The target directory is the project's own root, so
pathmust be the empty string, and the session must show up at the project root subpath:Why
session.pathis meant to hold the session directory relative to the project root (''means the project root), and the project session list filters on exactly that value via thesubpathquery parameter. The move handler appears to derive it with something equivalent topath.relative(project.worktree, directory)without normalizing path styles. Here the project root is stored POSIX (/home/<user>/probe) while the requested directory is Windows (D:/home/<user>/probe), so the relative path is computed across two unrelated roots and comes out as../../../home/<user>/probe.The same normalization gap was observed once on session creation rather than on move: a session created with a directory that did not resolve to a known project fell back to the
globalpseudo-project (worktree = '/') and receivedpath = '../../home/<user>'instead of''.Two related observations, in case they are of interest to the maintainers:
project.worktreeitself stores whatever git discovery returns, so a project discovered from a Cygwin shell keeps a POSIX root while every session directory stays Windows-form. All other projects on the machine are stored Windows-form, so this also means the same physical directory can be represented in two styles.POST /api/session/{sessionID}/move) is not mentioned in the public docs, although the/moveTUI command uses it.Impact
Silent loss of visibility: the move succeeds, the session disappears from the sidebar and from
opencode session listfor the target project, and neither reopening the project nor repeating the move brings it back — the row has to be corrected in the database (path = ''). This is the same failure mode that makes pre-migration sessions invisible.Suggested fix
Normalize the project root and the target directory to a single form before computing the relative path, and short-circuit to
''when both resolve to the same directory. Possibly also normalizeproject.worktreeto the platform's native form when it is stored.