Repository navigation
Description
Bug
POST /session/{id}/command returns HTTP 500 when the session's stored directory in the database points to a path that no longer exists (e.g. the project was moved or deleted after the session was created).
Steps to Reproduce
- Open a project at
/path/to/old-locationin opencode (TUI or web UI) - Send a command — works fine
- Move or delete the project directory:
mv /path/to/old-location /path/to/new-location - Restart opencode (
opencode web), open the web UI - The old session appears in the session list
- Type a message and send → HTTP 500 returned
noReply: truemessages (e.g./compact) succeed because they bypass the code path that validates the directory
Root Cause
The WorkspaceRoutingMiddleware (workspace-routing.ts:182) extracts session?.directory from the session's database row and sets it as the routing directory:
RequestPlan.Local({ directory: session?.directory || defaultDirectory(request, url) })
This directory is then used by InstanceContextMiddleware (instance-context.ts:29) to load the instance context via store.load({ directory }), which initializes a FileSystem service rooted at that path. When the directory no longer exists, FileSystem.realPath() fails with ENOENT.
Because SystemPrompt.environment() (system.ts:58-94) has signature Effect<string[]> (no error type), the Fail from FileSystem.realPath is converted to a Die defect. This Die bypasses the handler's Effect.mapError(() => new HttpApiError.BadRequest({})) (session.ts:338) — which only catches Fail causes — and hits the default 500 error handler.
Error chain:
SessionHttpApi.command
→ SessionPrompt.command → prompt → loop → ensureRunning → runLoop
→ SystemPrompt.environment()
→ ctx.directory = InstanceState.context.directory (from stale session row)
→ LocationServiceMap.get(ref) → FileSystem.realPath(stale directory) → ENOENT
→ Die (not Fail) → bypasses Effect.mapError(→BadRequest) → 500
Location in code
packages/opencode/src/server/routes/instance/httpapi/middleware/workspace-routing.ts:182—session?.directoryis the source of the stale pathpackages/opencode/src/server/routes/instance/httpapi/middleware/instance-context.ts:29—store.load({ directory })fails when directory doesnt existpackages/opencode/src/session/system.ts:58-94—SystemPrompt.environment()converts the error to a Diepackages/opencode/src/server/routes/instance/httpapi/handlers/session.ts:338—Effect.mapError(→BadRequest)insufficient for Die
Suggested Fix
Either:
A) Gracefully handle missing directory in SystemPrompt.environment()
Wrap the FileSystem.realPath call inside LocationServiceMap.get(ref) with Effect.catchAll so a missing reference directory is silently skipped instead of crashing the entire request. This is the more robust fix as it also handles other scenarios where a reference path becomes unreachable.
B) Allow the session directory to be updated
Add an API or CLI command to change a session's directory field in the database when the project moves. This would also help users who reorganize their filesystem.
C) Validate session directory at session list time
When listing sessions, detect if the stored directory is unreachable and surface a warning so the user knows which sessions are orphaned.
Workaround
Update the session's directory column in the SQLite database directly:
UPDATE session SET directory = '/path/to/new-location' WHERE id = '<session-id>';The database is located at ~/.local/share/opencode/opencode.db.
Activity
github-actions commentedon Jul 5, 2026
This issue might be a duplicate of existing issues. Please check:
session.directoryin DB), same HTTP 500/abort symptom when the path no longer existsIf this is the same underlying problem, consider adding your findings there (especially the
SystemPrompt.environment()→ Die defect chain you identified) rather than tracking it separately.robertn702 commentedon Jul 19, 2026
Confirming this on the desktop app (macOS, OpenCode Desktop 1.18.3,
ai.opencode.desktop) — with an extra wrinkle: in the desktop app the stale path lives in two places, and the second one poisons every launch.session.directory(same as this issue) — sessions bound to a deleted Superset worktree subfolder (/Users/robert/.superset/worktrees/540f8918-…/test-setup, removed when the worktree was cleaned up).'s persisted global state~/Library/Application Support/ai.opencode.desktop/opencode.global.dat→server.projects.local[].worktreeandserver.lastProject.local`, both still pointing at the deleted path.What I observed:
FileSystem.realPath(lastProject)from insideServer.listenand dies withPlatformError: NotFound … ENOENT(stack inapp.asar/out/main/chunks/node-*.js).GET /api/reference?directory=<deleted>→ HTTP 500.POST /session/{id}/prompt_asyncfor those sessions fails withCause([Die(PlatformError: NotFound: FileSystem.realPath (…))])— 200+ occurrences in~/.local/share/opencode/log/opencode.log. As analyzed above, theDiebypasses theBadRequestmapping, so the client gets a 500 at best.opencode.global.dat/ the stale sessions from the DB by hand.Building on the root-cause analysis above, I`'d suggest:
projects[].worktree/lastProjectat app boot and drop or flag missing paths before the sidecar resolves them.prompt_asyncdies this way, surface a session-level error in the chat UI (the event stream already carries enough to detect it).Hona commentedon Sep 2, 2026
Fixed for opencode2 launch via #46713
You can try the desktop beta here: https://github.com/anomalyco/opencode-beta