You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
'--continue' flag causes 'Unexpected server error' toast at boot (sessionID placeholder is literal 'dummy') #34144
Session-ID validation expects the ses_ prefix, so "dummy" throws before the subsequent useEffect hook navigates to the actual last session. Cosmetic — functionality still works — but the error toast is alarming to users.
Fix suggestion: pass undefined instead of "dummy", or skip initial-route validation when the session has not loaded yet.
changed the title [-] flag causes toast at boot (sessionID placeholder is literal "dummy")[/-][+]'--continue' flag causes 'Unexpected server error' toast at boot (sessionID placeholder is literal 'dummy')[/+]on Jun 27, 2026
Corroborating from an independent report (#43020, closed as dup of this): same Error: Expected a string starting with "ses", got "dummy" on 1.18.18, ~1 occurrence/day since ~Aug 6 2026. Context per log: the failing run immediately performs watcher-backend + project-copy-refresh work (background project indexing), concurrent real ses_* streams unaffected, stacks resolve only into bundled code (//root/chunk-*.js). Consistent with the hardcoded-placeholder-before-real-session-ID root cause.
Still reproducible on the V2 line: confirmed on 2.0.18 and 2.0.23 (Homebrew tap anomalyco/tap/opencode-v2, macOS arm64). The placeholder is still present in packages/tui/src/app.tsx on tags v2.0.18, v2.0.23, v2.0.24 and on dev:
opencode -c in a project that already has sessions
Error shown at boot: Expected a string starting with "ses" at ["sessionID"]
After dismissing it, the last session loads and works normally
Server log on 2.0.23: six requests fail in the same millisecond, right at boot, and none afterwards:
GET /api/session/dummy 400
GET /api/session/dummy/message 400
GET /api/session/dummy/inbox 400
GET /api/session/dummy/permission 400
GET /api/session/dummy/form 400
GET /api/session?parentID=dummy 400
level=WARN message="schema rejection" kind=Params reason="Expected a string starting with \"ses\"\n at [\"sessionID\"]"
level=WARN message="schema rejection" kind=Query reason="Expected a string starting with \"ses\"\n at [\"parentID\"]"
On 2.0.18, GET /api/experimental/session/dummy/terminal was also requested.
Observations
Some TUI code paths already guard against the placeholder (sessionID !== "dummy" checks around session tabs and the view/idle tracking), but the session view data loaders (messages, inbox, permissions, forms, child sessions) do not, so they hit the API before the real session ID is resolved.
opencode -s <ses_id> opens the same session without the error, so the issue is specific to the --continue initial route.
Workaround for users: opencode session list, then opencode -s <ses_id>.
Possible fixes: use undefined / a dedicated "resolving" route state instead of a fake session ID, or skip session data loading while the route still holds the placeholder.
Direct repro against the live server (same error the user sees):
$ curl -s -u opencode:<password> http://127.0.0.1:<port>/api/session/dummy
{"_tag":"InvalidRequestError","message":"Expected a string starting with \"ses\"\n at [\"sessionID\"]","kind":"Params"}
The placeholder is still sessionID: "dummy" in packages/tui/src/app.tsx (input.args.continue ? { type: "session", sessionID: "dummy" } : undefined), and the session route mounts against it before the continue effect resolves the real session, so every data endpoint gets a 400 on boot.
I have a local fix in progress: it keeps the placeholder route but skips all session data syncs (session get/messages/inbox/permission/form + child-list parentID) while sessionID === "dummy", so no request is made until the real session ID is known. Preparing a PR; will link here.
Still present in 2.1.3 (Termux musl build). Tight correlation: 2.1.3 installed 2026-10-22 08:30, and the first schema rejection in the server log lands at 08:37 boot with args ["-c"] — zero occurrences in the entire prior log under 2.1.2.
Server side, v2.1.3 background service:
WARN "schema rejection" kind=Params reason="Expected a string starting with \"ses\"\n at [\"sessionID\"]"
INFO "Sent HTTP response" http.url=/api/session/dummy http.status=400
INFO "Sent HTTP response" http.url=/api/session/dummy/inbox http.status=400
INFO "Sent HTTP response" http.url=/api/session/dummy/message http.status=400
INFO "Sent HTTP response" http.url=/api/experimental/session/dummy/terminal http.status=400
INFO "Sent HTTP response" http.url=/api/session?parentID=dummy http.status=400
INFO "Sent HTTP response" http.url=/api/session/dummy/permission http.status=400
INFO "Sent HTTP response" http.url=/api/session/dummy/form http.status=400
Surface error in the TUI: Expected a string starting with "ses" x at ["sessionID"]/. After the burst the run never issues a single real-session request, so the last session is not resumed (TUI falls back to a fresh session).
Workaround that does work: opencode -s ses_<id> (explicit --session) boots clean — no dummy requests, no rejections.
Thanks for the detailed report and root-cause analysis. I could reproduce this on 2.2.0.
Steps:
Create a session in a project.
Start opencode --continue while the TUI's "latest session" lookup is slow. The script below simulates this with a proxy that delays that one request by 3s.
What happens: the TUI first opens a placeholder session dummy and requests it (/api/session/dummy, /message, /permission, /form, …). The server rejects these, so a toast appears (Expected a string starting with "ses" at ["sessionID"]) and the TUI briefly drops to the home screen. A moment later it opens the real last session, as you described. The recording and screenshot below show it.
Latest dev (f43563b): the dummy placeholder is still in the code, and the same invalid requests are sent on every --continue start. The toast itself didn't appear in my 3 runs there, most likely because of startup timing, so it may show up only some of the time.
Related: #47556 (closed as a duplicate of this issue; it reports that OPENCODE_FAST_BOOT=1 makes it easier to trigger) and #43020 (same got "dummy" error in the logs).
recording.mp4
Reproduction script (drive.ts)
// Reproduces #34144: `opencode --continue` shows a session-ID validation error toast at boot// because the TUI seeds the session route with sessionID "dummy".//// Run: OPENCODE_DRIVE_MEDIA_DIR=./media opencode-drive run ./drive.ts// (set OC_DEV=/path/to/opencode/checkout to run from source instead of `opencode` on PATH)//// 1. The first TUI creates a session by submitting a prompt.// 2. A second, recorded TUI is launched with `--continue` against the same server.// It goes through a small proxy that delays the "latest session" lookup by 3s// (as on a slow machine or with a large history), so the placeholder route mounts first.import{Effect}from"effect"import{OpenCodeDriver}from"opencode-drive"import{chmodSync,mkdirSync,rmSync,writeFileSync}from"node:fs"constdir="/tmp/opencode/oc34144"constwrapper=`${dir}/oc-continue.sh`constflag=`${dir}/continue.flag`mkdirSync(dir,{recursive: true})rmSync(flag,{force: true})writeFileSync(`${dir}/slow-proxy.ts`,'// Forwards to the real server, delaying the session list request the TUI makes for --continue\n// (as on a slow machine or with a large session history), so the placeholder route mounts first.\nconst target = process.argv[2]!\nconst port = Number(process.argv[3])\nBun.serve({\n port,\n idleTimeout: 0,\n async fetch(req) {\n const url = new URL(req.url)\n const upstream = new URL(url.pathname + url.search, target)\n if (process.env.PROXY_LOG) console.error(req.method, url.pathname + url.search)\n if (url.searchParams.get("limit") === "1" || url.pathname.endsWith("/session/list")) await Bun.sleep(3000)\n const headers = new Headers(req.headers)\n headers.delete("host")\n return fetch(upstream, {\n method: req.method,\n headers,\n body: req.method === "GET" || req.method === "HEAD" ? undefined : await req.arrayBuffer(),\n redirect: "manual",\n // @ts-ignore\n decompress: false,\n })\n },\n})\n')writeFileSync(wrapper,'#!/bin/sh\n# Runs opencode (installed, or the OC_DEV checkout from source) and adds\n# --continue when launched as a TUI (i.e. with --server) once the drive script\n# has created the flag file (so the first TUI starts normally).\n# The TUI is pointed at slow-proxy.ts, which delays the latest-session lookup.\nif [ -n "$OC_DEV" ]; then\n set -- bun --conditions=browser --preload="$OC_DEV/packages/tui/node_modules/@opentui/solid/scripts/preload.js" "$OC_DEV/packages/cli/src/index.ts" "$@"\nelse\n set -- opencode "$@"\nfi\nif [ -f /tmp/opencode/oc34144/continue.flag ]; then\n case " $* " in *" --server "*)\n # Route this TUI through a proxy that delays the "latest session" lookup by 3s.\n port=$(( 40000 + $$ % 20000 ))\n out=""; prev=""\n for a in "$@"; do\n if [ "$prev" = "--server" ]; then\n bun /tmp/opencode/oc34144/slow-proxy.ts "$a" "$port" >>/tmp/opencode/oc34144/proxy.log 2>&1 &\n a="http://127.0.0.1:$port"\n fi\n out="$out \'$a\'"; prev="$a"\n done\n eval "set -- $out"\n sleep 1\n set -- "$@" --continue ;;\n esac\nfi\nexec "$@"\n')chmodSync(wrapper,0o755)exportdefaultOpenCodeDriver.use({opencode: {command: [wrapper]},project: {git: true,files: {"README.md": "hello\n"}},},({ ui, tuis })=>Effect.gen(function*(){yield*ui.submit("Say hello")yield*ui.waitFor("Say hello")yield*ui.screenshot("first-tui")// From now on, TUIs are launched with --continue (through the slow proxy).yield*Effect.sync(()=>writeFileSync(flag,""))constsecond=yield*tuis.launch({recording: true,viewport: {cols: 120,rows: 40}})yield*second.recording!.mark("opencode --continue")yield*second.ui.waitFor("Expected a string starting with",{timeout: 10000}).pipe(Effect.catchTag("UiWaitTimeoutError",()=>Effect.log("NO ERROR TOAST SEEN")))yield*second.ui.screenshot("continue-toast")yield*Effect.sleep(4000)yield*second.ui.screenshot("continue-after")}),)
Run it with opencode-drive: opencode-drive run ./drive.ts. It drives opencode from your PATH; set OC_DEV to the path of an OpenCode checkout to run that from source instead.
Activity
[-] flag causes toast at boot (sessionID placeholder is literal "dummy")[/-][+]'--continue' flag causes 'Unexpected server error' toast at boot (sessionID placeholder is literal 'dummy')[/+]satwareAG-ironMike commentedon Aug 17, 2026
Corroborating from an independent report (#43020, closed as dup of this): same
Error: Expected a string starting with "ses", got "dummy"on 1.18.18, ~1 occurrence/day since ~Aug 6 2026. Context per log: the failing run immediately performs watcher-backend + project-copy-refresh work (background project indexing), concurrent realses_*streams unaffected, stacks resolve only into bundled code (//root/chunk-*.js). Consistent with the hardcoded-placeholder-before-real-session-ID root cause.vilherda commentedon Oct 6, 2026
Still reproducible on the V2 line: confirmed on
2.0.18and2.0.23(Homebrew tapanomalyco/tap/opencode-v2, macOS arm64). The placeholder is still present inpackages/tui/src/app.tsxon tagsv2.0.18,v2.0.23,v2.0.24and ondev:Steps
opencode -cin a project that already has sessionsExpected a string starting with "ses" at ["sessionID"]Server log on 2.0.23: six requests fail in the same millisecond, right at boot, and none afterwards:
On 2.0.18,
GET /api/experimental/session/dummy/terminalwas also requested.Observations
sessionID !== "dummy"checks around session tabs and the view/idle tracking), but the session view data loaders (messages, inbox, permissions, forms, child sessions) do not, so they hit the API before the real session ID is resolved.opencode -s <ses_id>opens the same session without the error, so the issue is specific to the--continueinitial route.opencode session list, thenopencode -s <ses_id>.Possible fixes: use
undefined/ a dedicated "resolving" route state instead of a fake session ID, or skip session data loading while the route still holds the placeholder.bornmw commentedon Oct 10, 2026
Reproduced on
v0.0.0-pr-54107-202610102051(branchpr-54107, 2026-10-10, Linux x64) withopencode -c.Client:
opencode -cin a directory with existing sessions.Server log (the rejected placeholder fetches — one per session data endpoint):
Direct repro against the live server (same error the user sees):
The placeholder is still
sessionID: "dummy"inpackages/tui/src/app.tsx(input.args.continue ? { type: "session", sessionID: "dummy" } : undefined), and the session route mounts against it before the continue effect resolves the real session, so every data endpoint gets a 400 on boot.I have a local fix in progress: it keeps the placeholder route but skips all session data syncs (session get/messages/inbox/permission/form + child-list
parentID) whilesessionID === "dummy", so no request is made until the real session ID is known. Preparing a PR; will link here.DEAD1nsane commentedon Oct 22, 2026
Still present in 2.1.3 (Termux musl build). Tight correlation: 2.1.3 installed 2026-10-22 08:30, and the first
schema rejectionin the server log lands at 08:37 boot with args["-c"]— zero occurrences in the entire prior log under 2.1.2.Server side, v2.1.3 background service:
Surface error in the TUI:
Expected a string starting with "ses" x at ["sessionID"]/. After the burst the run never issues a single real-session request, so the last session is not resumed (TUI falls back to a fresh session).Workaround that does work:
opencode -s ses_<id>(explicit--session) boots clean — no dummy requests, no rejections.opencodejr commentedon Oct 29, 2026
Thanks for the detailed report and root-cause analysis. I could reproduce this on 2.2.0.
Steps:
opencode --continuewhile the TUI's "latest session" lookup is slow. The script below simulates this with a proxy that delays that one request by 3s.What happens: the TUI first opens a placeholder session
dummyand requests it (/api/session/dummy,/message,/permission,/form, …). The server rejects these, so a toast appears (Expected a string starting with "ses" at ["sessionID"]) and the TUI briefly drops to the home screen. A moment later it opens the real last session, as you described. The recording and screenshot below show it.Latest
dev(f43563b): thedummyplaceholder is still in the code, and the same invalid requests are sent on every--continuestart. The toast itself didn't appear in my 3 runs there, most likely because of startup timing, so it may show up only some of the time.Related: #47556 (closed as a duplicate of this issue; it reports that
OPENCODE_FAST_BOOT=1makes it easier to trigger) and #43020 (samegot "dummy"error in the logs).recording.mp4
Reproduction script (drive.ts)
Run it with opencode-drive:
opencode-drive run ./drive.ts. It drivesopencodefrom yourPATH; setOC_DEVto the path of an OpenCode checkout to run that from source instead.