Skip to content

SortableWorkspace session list empty due to store.path.directory not bootstrapped #21441

Closed as not planned

Description

@chenyy9527

Description

Session list is completely empty on Windows web UI. Skeleton briefly appears on refresh then disappears. Verified via DevTools that GET /session?directory=...&roots=true returns sessions correctly, so this is a client-side store management issue caused by directory format mismatch creating duplicate child stores. child-store.ts creates separate stores for the same physical directory when path formats differ (D:/... vs D:\...), exceeding MAX_DIR_STORES=30 and triggering eviction. loadSessions writes to a disposed store — session data is lost entirely.

Plugins

none

OpenCode version

0.0.0-dev-202604080233 source build (dev branch)

Steps to reproduce

  1. Open web UI on Windows
  2. Have multiple projects or switch between sessions
  3. Observe session list is empty
  4. DevTools Network shows API returns sessions but sidebar renders none

Screenshot and/or share link

No response

Operating System

Windows 11

Terminal

Git Bash

Activity

  1. github-actions commented on Apr 8, 2026

    @github-actions
    Contributor

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

    If your issue is distinct from these, please add a note explaining the difference.

  2. chenyy9527 commented on Apr 8, 2026

    @chenyy9527
    Author

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

    If your issue is distinct from these, please add a note explaining the difference.

    Distinct from both. #14570 is about filtering — sessions exist in store but get filtered by workspaceKey() mismatch. #16744 is about missing sessions after upgrade. This is a store management bug: child-store.ts creates separate stores for the same physical directory when path formats differ (D:/... vs D:...), exceeding MAX_DIR_STORES=30 and triggering eviction. loadSessions writes to a disposed store — data is lost, not just filtered. Affects both LocalWorkspace and SortableWorkspace on Windows.

  3. chenyy9527 commented on Apr 8, 2026

    @chenyy9527
    Author

    Data Flow Path Comparison

    Session Write Path

    Client creates session
      → SDK client directory parameter (may contain backslashes)
      → Server-side router.ts middleware Filesystem.resolve (normalized to forward slashes)
      → InstanceState.directory (forward slashes)
      → Session.createNext → input.directory (forward slashes)
      → DB session.directory (forward slashes)
    

    Session Query Path (The Problem)

    Client loadSessions
      → globalSDK.client.session.list({ directory: "D:\project\workspace" })
      → Server-side Session.list
      → eq(SessionTable.directory, "D:\project\workspace")  // exact match
      → DB stores "D:/project/workspace"                    // no match
      → Returns empty
    

    Two Request Paths on Page Refresh

    Path A (forward slashes, finds data):
      LayoutProvider.onMount
      → server.projects.list() → project.worktree = "D:/project/workspace"
      → loadSessions("D:/project/workspace")
      → session.list({ directory: "D:/project/workspace" })
      → eq(directory, "D:/project/workspace") ✅ Match
    
    Path B (backslashes, finds no data):
      DirectoryLayout → decode64(params.dir) = "D:\project\workspace"
      → SDKProvider → sdk.directory = "D:\project\workspace"
      → SyncProvider → globalSync.child → bootstrapInstance
      → loadSessions("D:\project\workspace")
      → session.list({ directory: "D:\project\workspace" })
      → eq(directory, "D:\project\workspace") ❌ No match
    
  4. chenyy9527 commented on Apr 8, 2026

    @chenyy9527
    Author

    Typical Scenario: API Returns Data but Frontend Doesn't Display

    Even when the Path A (forward slashes) API request successfully returns session data, the frontend may still not display it. This is because data is written to the wrong store.

    Complete Sequence

    1. Page loads, loadSessions triggered simultaneously from two sources
    
       Path A: loadSessions("D:/project/workspace")   ← LayoutProvider.onMount
       Path B: loadSessions("D:\project\workspace")   ← SyncProvider → bootstrapInstance
    
    2. Inside loadSessions, ensureChild(directory) creates/gets child store
    
       Path A: children["D:/project/workspace"] → creates store_A
       Path B: children["D:\project\workspace"] → creates store_B (same directory, different key)
    
    3. API returns data, written to respective stores
    
       Path A: session.list({ directory: "D:/project/workspace" }) → returns 9 sessions
               → writes to store_A.session = [9 sessions]
    
       Path B: session.list({ directory: "D:\project\workspace" }) → returns 0 (server-side mismatch)
               → store_B.session = []
    
    4. Which store does the frontend render read from?
    
       currentSessions() memo → visibleSessionDirs() → currentDir() → route().dir
       → route() memo: globalSync.peek("D:\project\workspace") → looks up children["D:\project\workspace"]
       → gets store_B (empty)
    
       Or if route uses the forward slash path:
       → gets store_A (has data), but roots() filter:
         workspaceKey(session.directory) === workspaceKey(store.path.directory)
         store.path.directory is "" at this moment (bootstrap incomplete) → all filtered out
    

    Root Conflict

    Stage Path Format Used Result
    LayoutProvider triggers loadSessions D:/project/workspace (forward slashes) Creates store_A, writes 9 sessions
    SyncProvider triggers bootstrapInstance D:\project\workspace (backslashes) Creates store_B, store_B.session is empty
    Frontend renders currentSessions Depends on path in route() May get store_A or store_B
    roots() filtering store.path.directory may not be ready Even if store_A is retrieved, all may be filtered out

    Three overlapping issues cause the empty session list:

    1. Store Split — Same directory with two keys, data scattered across two stores
    2. Server-side Mismatch — Backslash query returns empty
    3. Client-side Filtering Failure — roots() filters out all sessions when store.path.directory is not ready
  5. chenyy9527 commented on Apr 8, 2026

    @chenyy9527
    Author

    After unifying path normalization behavior, unmigrated historical data will cause users to see an empty session list despite data still existing in the database. Is this "data not lost but invisible" state acceptable, or must we implement query-layer compatibility as a fallback?

  6. added a commit that references this issue on Apr 8, 2026
    4406df3
  7. wlclass commented on Apr 9, 2026

    @wlclass

    被这个问题困扰半个月了。很痛苦

  8. wlclass commented on Apr 9, 2026

    @wlclass

    请问有没有临时解决办法?

  9. removed
    bugSomething isn't working
    webRelates to opencode on web / desktop
    on May 3, 2026
  10. github-actions commented on Jul 6, 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