opencode icon indicating copy to clipboard operation
opencode copied to clipboard

Session lookup fails with NotFoundError when PTY spawned from non-git directory context

Open Skeptomenos opened this issue 20 hours ago • 1 comments

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

  1. Start OpenCode in a git repository (e.g., /Users/davidhelmus/Repos/SomeProject)
  2. Have a session running with a project-specific ID (derived from git root commit)
  3. Spawn a PTY session using pty_spawn
  4. In the nested PTY, ask the LLM to spawn another PTY session or perform session-related operations
  5. The nested OpenCode instance runs from home directory (~), which has no .git
  6. 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

  1. PTY Creation (src/pty/index.ts):

    const cwd = input.cwd || Instance.directory  // Uses current Instance context
    
  2. Server Request Handling (src/server/server.ts line 252):

    let directory = c.req.query("directory") || c.req.header("x-opencode-directory") || process.cwd()
    

    When process.cwd() is ~/, the Instance gets projectID: "global".

  3. Session Lookup (src/session/index.ts line 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

  1. Session-to-project index: Create a global index mapping session IDs to their project IDs, enabling cross-project lookups.

  2. Fallback search: When a session isn't found in the current project, search other project directories.

  3. Preserve directory context: When spawning PTY sessions, pass the original directory context via environment variable or header so nested operations use the correct project.

  4. 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() uses Instance.project.id for lookup (line 231)
  • src/project/instance.ts - Instance context management
  • src/pty/index.ts - PTY creation uses Instance.directory for cwd (line 104)
  • src/server/server.ts - Request handling determines directory context (line 252)

Skeptomenos avatar Jan 14 '26 20:01 Skeptomenos