{"record":{"id":"a535a9062dddcf66","repo":"deepseek-ai/deepseek-harness","slug":"unknown-session-sessionid","errorCode":null,"errorMessage":"unknown session \"${sessionId}\"","messagePattern":"unknown session \"(.+?)\"","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/client/ui-workspace/src/client/index.ts","lineNumber":84,"sourceCode":"  // source identity): true while the surface's directory-flow hole is filled.\n  const flowSource = (hole: 'sidebar.workspaces.directoryFlow' | 'conversation.hero.workspace.directoryFlow'): HostObservable<boolean> => ({\n    getSnapshot: () => ctx.slots.entries(hole).length > 0,\n    subscribe: listener => ctx.slots.subscribe(hole, listener),\n  })\n  const browserFlowSource = flowSource('sidebar.workspaces.directoryFlow')\n  const pickerFlowSource = flowSource('conversation.hero.workspace.directoryFlow')\n  const browserInjected = (): WorkspaceBrowserInjected => ({\n    // Explicit group actions keep their target; unscoped New Session inherits\n    // the current Session Workspace before the recent-Workspace fallback.\n    startSession: (workspaceId) => { ctx.workspaces.startSession(workspaceId) },\n    open: (sessionId) => { ctx.sessions.open(sessionId) },\n    searchSessions,\n    searchResultLimit: ctx.sessions.searchResultLimit,\n    renameSession: async (sessionId, title) => {\n      // Row → session-face hop: rename is a per-session verb (ISession), not\n      // a list-service verb; the binding resolves any listed session.\n      const session = ctx.sessions.binding(sessionId)?.session\n      if (session === undefined) throw new Error(`unknown session \"${sessionId}\"`)\n      const result = await session.rename(title)\n      if (!result.ok) throw new Error(result.error.message)\n    },\n    forkSession: (sessionId) => {\n      ctx.sessions.fork({ sessionId, increaseTitle: true })\n        .then((childId) => { ctx.sessions.open(childId) })\n        .catch(() => {\n          // Fork or child-rename failure keeps the current selection.\n        })\n    },\n    renameWorkspace: async (workspaceId, title) => { await ctx.workspaces.rename(workspaceId, title) },\n    deleteWorkspace: async (workspaceId) => { await ctx.workspaces.delete(workspaceId) },\n    insertWorkspaceBefore: async (workspaceId, beforeWorkspaceId) => {\n      await ctx.workspaces.insertBefore(workspaceId, beforeWorkspaceId)\n    },\n    archiveSession: async (sessionId) => { await ctx.workspaces.archiveSession(sessionId) },\n    insertSessionBefore: async (workspaceId, sessionId, beforeSessionId) => {\n      await ctx.workspaces.insertSessionBefore(workspaceId, sessionId, beforeSessionId)","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/deepseek-ai/deepseek-harness/blob/b150a551b8d465e31e418e1b2eaf5e79bbb7d28e/packages/client/ui-workspace/src/client/index.ts#L66-L102","documentation":"Thrown by the ui-workspace browser plugin's renameSession callback in the dsh web client. Rename is a per-session verb on the ISession face, not a sessions-list verb, so the callback first resolves the id through ctx.sessions.binding(sessionId). When the binding registry holds no live binding for that id, rename cannot proceed and the id is echoed verbatim.","triggerScenarios":"Calling the injected renameSession(sessionId, title) (committing a rename in the workspace browser) with an id that has no binding: the session was closed or deleted on the host after its row was rendered, the id came from a stale search result, or the sessions list refreshed and dropped the row between render and commit.","commonSituations":"Session deleted from another tab or client while the rename dialog was open; reconnect dropped live bindings; passing a workspace id where a session id is expected; ids captured from an old searchResults array.","solutions":["Re-run the session list or search so the row and its binding are current, then retry the rename.","Pass the session id taken from the live row at action time, not from stale component state or an old search result.","If concurrent deletion is normal in your flow, catch this error, drop or deselect the row, and refresh — do not retry blindly.","Verify you are not passing a workspace id; workspace renames go through ctx.workspaces.rename, not renameSession."],"exampleFix":"// before — id captured when the row rendered; may be stale by commit time\nawait renameSession(row.id, title)\n\n// after — re-resolve the binding at action time; refresh when it is gone\nconst session = sessions.binding(row.id)?.session\nif (session === undefined) { await refreshSessions(); return }\nawait session.rename(title)","handlingStrategy":"validation","validationCode":"const binding = sessions.binding(sessionId)\nif (binding?.session === undefined) {\n  await refreshSessions() // row is stale; re-sync before acting\n  return\n}\nawait binding.session.rename(title)","typeGuard":"function isBoundSession(sessions: SessionsService, id: string): boolean {\n  return sessions.binding(id)?.session !== undefined\n}","tryCatchPattern":"try {\n  await renameSession(sessionId, title)\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('unknown session')) {\n    // stale row: drop/deselect it and refresh the list; do not retry blindly\n    await refreshSessions()\n  } else throw err\n}","preventionTips":["Derive session ids from live rows at action time, never from cached search results.","Refresh the session list after reconnect or window focus.","Disable the rename affordance for rows whose binding is already gone."],"tags":["session","ui-workspace","rename","stale-id","web-client"],"backgroundTag":"invalid-session-id","analyzedSha":"b150a551b8d465e31e418e1b2eaf5e79bbb7d28e","analyzedAt":"2026-08-24T18:12:29.105Z","schemaVersion":2},"datasetVersion":"2026-08-24T22:17:12.610Z"}