Skip to content

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

Description

@Skeptomenos

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)

Activity

  1. github-actions commented on Jan 14, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    Feel free to ignore if none of these address your specific case.

  2. oussamadouhou commented on Jan 19, 2026

    @oussamadouhou

    Additional Findings from SDK/Server Debugging

    I've been debugging this exact issue when using opencode serve with SDK clients and found additional context that may help.

    SDK Client Directory Header Gap

    The @opencode-ai/sdk client only sends x-opencode-directory header when directory is configured at client creation time. Individual API calls don't consistently pass directory context, even when the caller knows the correct directory.

    Affected operations:

    Operation Passes Directory?
    session.create ✅ Yes (query param)
    session.get ❌ No
    session.prompt ❌ No
    session.messages ❌ No

    Reproduction via opencode serve

    1. Start opencode serve in project A (e.g., /home/user/projects/myapp)
    2. Connect via SDK from a different project context (project B)
    3. Create a session - stored in correct project dir:
      ~/.local/share/opencode/storage/session/eb6c255.../ses_xxx.json
      
    4. Subsequent session.get or session.messages fails with NotFoundError:
      Resource not found: ~/.local/share/opencode/storage/session/global/ses_xxx.json
      

    Suggested Fix Approaches

    Option A: Server-side session resolution (preferred)
    When looking up a session by ID, resolve the correct project directory:

    1. Maintain a lightweight session-to-projectID index, OR
    2. Search across project directories if not found in current context, OR
    3. Store sessions in a flat structure with projectID embedded in filename

    Option B: SDK/API enhancement
    Allow directory parameter on all session operations (get, prompt, messages), not just create. This pushes the burden to callers but would unblock SDK users.

    I've submitted PR #9474 implementing Option A with caching for performance.

  3. added a commit that references this issue on Jan 19, 2026
    bccbfc1
  4. benoitheinrich commented on Feb 4, 2026

    @benoitheinrich

    Additional confirmation: initializing a git repo does not resolve this.

    Context

    • Environment: OpenCode Desktop/Web connected to external server at http://127.0.0.1:4096
    • Version: v1.1.51
    • I initialized a git repository in the directory where I launch OpenCode.

    Observation

    • Even after git init, session lookups still resolve under session/global/... and trigger NotFoundError when reading by session ID.
    • This suggests the issue persists beyond the non-git/PTY scenario discussed here when using an external server/Desktop/Web setup.

    Reference

  5. sim590 commented on Apr 5, 2026

    @sim590

    I encountered a related case that isn't explicitly described in this issue:

    1. Start opencode in a directory without a Git repository
    2. Session is created under the global project (as expected)
    3. During the session, initialize a Git repo (git init + first commit) — in my case, the agent itself created the repo as part of the workflow
    4. Close OpenCode
    5. Reopen OpenCode in the same directory — it now detects the Git repo and resolves to a new project ID
    6. The previous session is not visible in /sessions because it's still associated with the global project

    The session data is not lost — it's still in the SQLite database (~/.local/share/opencode/opencode.db) under project_id = 'global'. I was able to recover it by manually updating the project_id and directory fields in the session table.

    This seems like a case where OpenCode should either:

    • Migrate existing global sessions when a Git repo is detected in the same working directory, or
    • At minimum, detect that sessions exist under global for the current directory and surface them in /sessions

    This is consistent with the server-side session resolution approach suggested in PR #9474 (Option A).

    Environment: OpenCode v1.3.3 on Linux (TUI)

  6. sim590 commented on Apr 7, 2026

    @sim590

    Additional scenario: git init during an active session

    I encountered a case where sessions become orphaned after initializing a Git repo during an active session.

    Steps to reproduce:

    1. Start opencode in a directory without a Git repository (e.g., ~)
    2. A session is created under the global project with directory set to the launch directory (e.g., /home/simon)
    3. During the session, create a subdirectory and initialize a Git repo (e.g., git init + first commit in ~/prog/tewst) — in my case, the agent itself created the repo as part of the workflow
    4. Close OpenCode
    5. Reopen OpenCode in the new Git repo directory (~/prog/tewst)
    6. The previous session is not visible in /sessions

    Root cause:

    OpenCode already has an automatic migration mechanism in project.ts that reassigns global sessions to the correct project at startup. However, it relies on an exact match between session.directory and project.worktree:

    db.update(SessionTable)
      .set({ project_id: data.id })
      .where(and(
        eq(SessionTable.project_id, ProjectID.global),
        eq(SessionTable.directory, data.worktree)
      ))
      .run()

    In this scenario, session.directory is /home/simon (the launch directory) while project.worktree is /home/simon/prog/tewst. The strict equality check fails and the session remains orphaned in global.

    Confirmed twice:

    • Once with a real project where the agent created a Civ 7 modding plugin
    • Once with a deliberate reproduction test (~/prog/tewst)

    In both cases, the session data was intact in the SQLite database under project_id = 'global' and recoverable via a manual UPDATE.

    Suggested fix:

    The migration condition in project.ts could be relaxed to also match sessions where session.directory is a parent directory of project.worktree, not just an exact match. A variant of this scenario (renaming the project directory) would also benefit from a broader matching strategy.

    Additionally, a cleanup mechanism for orphaned sessions (where session.directory points to a path that no longer exists) could reassign them to the user's home directory so they remain accessible.

    Environment: OpenCode v1.3.3 on Linux (TUI)

  7. sim590 commented on Apr 18, 2026

    @sim590

    I've opened #23248 which documents two specific reproduction cases for orphaned sessions caused by directory renaming. The root cause is related — the directory field on sessions is never updated when the project worktree changes.

  8. github-actions commented on Jun 18, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions