Skip to content

Web UI: opening session URL directly renders blank page (no timeline) #43667

Closed

Description

@nico2525nn

Summary

Opening a session URL directly in the Web UI (/server/{serverKey}/session/{sessionID}) boots the app, but the main content area stays empty and the message timeline is never rendered. Navigating from the project/session list works in the user's browser, but the direct URL fails silently.

Environment

Reproduction

  1. Create a session: opencode2 api post /api/session -d '{"location":{"directory":"/home/nico"}}'
  2. Open http://localhost:49374/server/{serverKey}/session/{sessionID} in a fresh browser context
  3. The app boots (BETA badge, Home, New session buttons appear) but main area stays empty
  4. Console shows no errors; all asset/API requests return 200 (/api/session/active, /api/project, /api/worktree/*)

Expected Behavior

The session page should render the session timeline (messages) after loading the URL directly.

Actual Behavior

The app boots but never fetches/renders the session's messages (/api/session/{id}/... is never requested). The main area is blank with no error, toast, or failure message.

Additional Context

Suggested investigation

  • Validate that the session route forces a session sync / message fetch on direct navigation
  • Check why the session lineage is not populated when entering via URL without the sidebar/project flow

Activity

  1. nico2525nn commented on Aug 20, 2026

    @nico2525nn
    Author

    Follow-up investigation (verified with a real browser via Chrome DevTools MCP):

    The blank page is NOT about browser profiles or "fresh context" by itself. The root cause is a split between two state layers:

    1. Session content / messages are server-side (DB) — they display in any browser/profile/access as long as the URL is correct. Confirmed: a private tab and a fresh (incognito-style) browser profile could both display the session normally.

    2. The home/project sidebar list is client-side localStorage, persisted per server identity (the base64 server URL in the route), in packages/app/src/context/server.tsx (projects: Record<string, StoredProject[]> persisted under Persist.global("server", ["server.v3"]), consumed by home.project.forServer(conn)).

    Because the same backend is exposed under multiple URLs (localhost and a Tailscale hostname), each URL becomes a separate server identity in the Web UI:

    • Projects added under one URL do not appear under another (each is a separate localStorage entry).
    • Re-adding a project on the same URL makes it appear (it was already the same localStorage scope).
    • A brand-new browser profile has empty localStorage, so the project list is empty until projects are (re-)added — but the session itself still loads, since sessions come from the server DB.

    This also explains the "projects disappear / not synced" reports: the sidebar is filtering by which server identity the current browser connection is bound to.

    Suggested fix direction: key the home project list by a server-side identifier (e.g., projectID or the canonical worktree path) instead of the URL-derived server key, or merge project lists for connections pointing at the same backend, so the sidebar is consistent regardless of which URL (localhost vs tailnet hostname) is used to reach the same server.

  2. TomKalina commented on Aug 26, 2026

    @TomKalina

    Reproducing on v1.18.23 with a different symptom than @nico2525nn's follow-up describes — direct URL navigation does NOT load the requested session's messages, even though the server-side API has them.

    Repro:

    1. Have a session ses_A (e.g. created via POST /api/session + a prompt) on a server at http://100.97.238.38:4096. Server detail endpoint returns it fine: GET /api/session/ses_A → 200 with data.location.directory = "/Users/.../dev".
    2. In a fresh browser context (or one where http://100.97.238.38:4096 is in localStorage as a known server with at least one project added), open the direct URL the UI itself generates when navigating from the sidebar:
      http://100.97.238.38:4096/server/{base64("http://100.97.238.38:4096")}/session/ses_A
    3. UI boots, route matches /server/:key/session/:id, then the URL bar changes to /server/{...}/session/ses_B where ses_B is some other session (in my case ses_fc2934cc1ffeEHx0xfH0t04ZhI — title "New session - 2026-08-26T09:35:29.091Z", created 2026-08-26T09:35:29, also directory=/Users/.../dev). The main area shows the empty "New session" editor; no /api/session/ses_A/message request is made.

    Expected: UI loads ses_A and renders its messages.
    Actual: UI redirects to a different session ID and shows an empty editor. The requested session's messages are never fetched.

    Differs from @nico2525nn's follow-up: they reported "the session itself still loads, since sessions come from the server DB" — i.e. only the sidebar/projects list was broken on URL navigation. In my repro the session itself is also dropped and replaced with a different one. Possibly version-specific (mine 1.18.23 vs theirs 0.0.0-beta-17728).

    Workaround I'm shipping for my dashboard: a custom modal session viewer that proxies GET /api/session/{id} and GET /api/session/{id}/message and renders the timeline inline. Would prefer to drop it once this is fixed.

  3. TomKalina commented on Aug 26, 2026

    @TomKalina

    Additional repro detail (v1.18.23): navigating via the sidebar session list does render messages correctly, but only the direct URL navigation breaks.

    Steps:

    1. Open http://100.97.238.38:4096/ → UI boots, redirects to /server/{key}/session/ses_X. Sidebar shows session list fine. But main area is empty (just the "New session" editor, no messages for ses_X).
    2. Click on any session in the sidebar list → UI loads it correctly: messages render, scroll works, all good.
    3. Copy the URL bar from step 2 (/server/{key}/session/ses_Y) and paste into a fresh tab → UI boots, lands on the correct route, but main area is empty again — no /api/session/{id}/message request fired.

    So the URL→route matching itself seems to work (no "Invalid server route" error, no redirect to a different session), but the message-fetching effect is not triggered on direct navigation. It only fires when the route transition originates from inside the SPA (sidebar click).

    Server logs show the /api/session/{id} (detail) request is made on both paths, but /api/session/{id}/message only on the sidebar-click path.

    Hope that helps narrow the scope.

  4. nico2525nn commented on Aug 28, 2026

    @nico2525nn
    Author

    Resolved in build 0.0.0-beta-18414 (verified): opening a session URL directly (/server/:serverKey/session/:id) now renders the session (header, composer, and message timeline) as expected. Closing as resolved.

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions