Session lookup fails with NotFoundError when PTY spawned from non-git directory context
Bug Description
When spawning a PTY session from within OpenCode, and then asking the LLM in that nested PTY to perform operations that require session lookup, a NotFoundError is thrown because the session is looked up in the wrong project directory.
Error Message
NotFoundError: NotFoundError
data: {
message: "Resource not found: /Users/davidhelmus/.local/share/opencode/storage/session/global/ses_4423845abffeKHmns5X1nuyqZ6.json",
},
at <anonymous> (src/storage/storage.ts:204:15)
Steps to Reproduce
- Start OpenCode in a git repository (e.g.,
/Users/davidhelmus/Repos/SomeProject) - Have a session running with a project-specific ID (derived from git root commit)
- Spawn a PTY session using
pty_spawn - In the nested PTY, ask the LLM to spawn another PTY session or perform session-related operations
- The nested OpenCode instance runs from home directory (
~), which has no.git - Error occurs when trying to look up the original session
Detailed Replication Guide
Prerequisites
- OpenCode installed (tested on v1.1.20)
- A git repository to work in
- Home directory (
~) should NOT be a git repository
Step-by-Step Replication
1. Verify your home directory is not a git repo
ls -la ~/.git
# Should return: No such file or directory
2. Start OpenCode in a git repository
cd ~/Repos/SomeProject # Any git repo
opencode
3. Note your session details
The session will be created with:
- A session ID like
ses_4423845abffeKHmns5X1nuyqZ6 - A project ID derived from the git root commit (e.g.,
be64773222be3caddeccc7e18daa90049882de9e) - Stored at:
~/.local/share/opencode/storage/session/{projectID}/{sessionID}.json
4. Spawn a PTY session
Ask the LLM to spawn a PTY:
Spawn a PTY session for me
The LLM will use pty_spawn tool. The PTY's working directory defaults to Instance.directory (the current project directory).
5. In the PTY, change to home directory and trigger session lookup
This is the key step. When the PTY runs commands from a non-git directory, and those commands need to look up the parent session:
cd ~
# Now any OpenCode operation that needs session context will fail
6. Trigger the error
Ask the LLM to perform any operation that requires looking up the session, such as:
- Spawning another PTY session (which references
parentID) - Any subagent task that needs session context
Why This Happens
When OpenCode runs from ~/ (no .git), the Project.fromDirectory() function returns:
{
id: "global",
worktree: "/",
sandbox: "/",
}
But the original session was stored under the git project's ID, not "global".
Verification Commands
Check where your session actually exists:
# Find the session file
find ~/.local/share/opencode/storage/session -name "ses_YOUR_SESSION_ID.json"
# It will be in a project-specific directory like:
# ~/.local/share/opencode/storage/session/be64773.../ses_xxx.json
# NOT in:
# ~/.local/share/opencode/storage/session/global/ses_xxx.json
Check the session's projectID:
cat ~/.local/share/opencode/storage/session/*/ses_YOUR_SESSION_ID.json | jq '.projectID'
# Returns the git root commit hash, e.g., "be64773222be3caddeccc7e18daa90049882de9e"
Root Cause Analysis
Session Storage Architecture
Sessions are stored in project-scoped directories:
~/.local/share/opencode/storage/session/{projectID}/{sessionID}.json
For git repositories, projectID is the root commit hash (e.g., be64773222be3caddeccc7e18daa90049882de9e).
For non-git directories, projectID is "global".
The Bug
When a nested PTY session runs from a directory without a git repo (like home directory), Project.fromDirectory() returns id: "global":
// project.ts lines 164-169
return {
id: "global",
worktree: "/",
sandbox: "/",
vcs: Info.shape.vcs.parse(Flag.OPENCODE_FAKE_VCS),
}
Then when looking up a session, it uses the current Instance.project.id:
// session/index.ts line 231
const read = await Storage.read<Info>(["session", Instance.project.id, id])
This causes the lookup to search in session/global/ instead of the actual project directory where the session was created.
Code Flow That Triggers the Bug
-
PTY Creation (
src/pty/index.ts):const cwd = input.cwd || Instance.directory // Uses current Instance context -
Server Request Handling (
src/server/server.tsline 252):let directory = c.req.query("directory") || c.req.header("x-opencode-directory") || process.cwd()When
process.cwd()is~/, the Instance getsprojectID: "global". -
Session Lookup (
src/session/index.tsline 231):const read = await Storage.read<Info>(["session", Instance.project.id, id]) // Instance.project.id is "global", but session is stored under actual project ID
Example
| Context | Project ID | Lookup Path | Result |
|---|---|---|---|
| Original session (in git repo) | be64773... |
session/be64773.../ses_442...json |
✅ File exists |
Nested PTY (from ~/) |
global |
session/global/ses_442...json |
❌ NotFoundError |
Environment
- OpenCode Version: 1.1.20
- OS: macOS (darwin)
- Shell: zsh
Suggested Fixes
-
Session-to-project index: Create a global index mapping session IDs to their project IDs, enabling cross-project lookups.
-
Fallback search: When a session isn't found in the current project, search other project directories.
-
Preserve directory context: When spawning PTY sessions, pass the original directory context via environment variable or header so nested operations use the correct project.
-
Include project ID in session references: When session IDs are passed between contexts (e.g.,
parentID), include the project ID.
Workaround
Ensure PTY sessions that will run OpenCode commands are spawned from within a git repository directory, not from home or other non-git directories. When using pty_spawn, explicitly set the cwd parameter to the project directory.
Related Code
-
src/storage/storage.ts-withErrorHandling()throws NotFoundError (line 204) -
src/project/project.ts-fromDirectory()determines project ID (lines 47-170) -
src/session/index.ts-get()usesInstance.project.idfor lookup (line 231) -
src/project/instance.ts-Instancecontext management -
src/pty/index.ts- PTY creation usesInstance.directoryfor cwd (line 104) -
src/server/server.ts- Request handling determines directory context (line 252)