{"record":{"id":"b6b6a16b1322158b","repo":"siyuan-note/siyuan","slug":"failed-to-remove-agent-session","errorCode":null,"errorMessage":"Failed to remove agent session","messagePattern":"Failed to remove agent session","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"app/src/layout/dock/agent/SessionStore.ts","lineNumber":203,"sourceCode":"            }\n            return {revision, session: resp.data?.session};\n        };\n        const save = previous ? previous.then((result) => persist(result.revision)) : persist(baseRevision);\n        sessionSaveQueues.set(snapshot.id, save);\n        try {\n            return await save;\n        } finally {\n            if (sessionSaveQueues.get(snapshot.id) === save) {\n                sessionSaveQueues.delete(snapshot.id);\n            }\n        }\n    },\n\n    async remove(id: string): Promise<void> {\n        await waitForPendingSave(id);\n        const resp = await fetchSyncPost(API + \"/removeSession\", {id}, APP_HEADER) as {code: number; msg?: string};\n        if (!resp || resp.code !== 0) {\n            throw new Error(resp?.msg || \"Failed to remove agent session\");\n        }\n        sessionRevisions.delete(id);\n        sessionRuntimeRevisions.delete(id);\n    },\n\n    async rename(id: string, newTitle: string): Promise<void> {\n        const session = await this.load(id);\n        if (!session) { return; }\n        session.title = newTitle;\n        await this.save(session);\n    },\n\n    async setPermission(id: string, permissionMode: AgentPermissionMode): Promise<AgentPermissionMode> {\n        await waitForPendingSave(id);\n        const resp = await fetchSyncPost(API + \"/setPermission\", {\n            sessionID: id,\n            permissionMode,\n        }, APP_HEADER) as {code: number; msg?: string; data?: {permissionMode?: AgentPermissionMode}};","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/app/src/layout/dock/agent/SessionStore.ts#L185-L221","documentation":"Thrown by SessionStore.remove() after waitForPendingSave(id) when POST /api/ai/agent/removeSession returns null or a non-zero code. The pending save is awaited first so the server is not asked to delete a session that still has an in-flight write, but a non-zero reply (e.g. session already gone, permission denied) still trips this guard.","triggerScenarios":"Removing a session id that was already deleted on the server, removing while the kernel rejects the request (auth/permission), removing when the kernel process is down, or removing a session whose id is malformed.","commonSituations":"User clicks delete on two clients simultaneously; one succeeds and the second hits a non-zero 'not found' code; workspace switched but the UI still holds stale ids; kernel restart between load and remove.","solutions":["Treat a 'not found' / already-removed reply as success: clear local caches (sessionRevisions/sessionRuntimeRevisions) and remove the row from the UI.","Surface resp.msg so the user sees the kernel's actual reason instead of the generic string.","Verify the kernel is reachable and the session id matches the listing returned by /lsSessions before issuing remove.","Retry once after re-establishing the WebSocket if the failure was a transient transport drop."],"exampleFix":"// before\nawait SessionStore.remove(id);\n// after\ntry {\n    await SessionStore.remove(id);\n} catch (e) {\n    if (/not found|already/i.test(e.message)) { sessionRevisions.delete(id); sessionRuntimeRevisions.delete(id); }\n    else { showMessage(e.message); throw e; }\n}","handlingStrategy":"try-catch","validationCode":"if (!id || typeof id !== 'string') throw new Error('remove: id required');\nconst exists = (await SessionStore.list({keyword: id})).sessions.some(s => s.id === id);","typeGuard":"function isSessionId(v: unknown): v is string { return typeof v === 'string' && v.length > 0; }","tryCatchPattern":"try { await SessionStore.remove(id); }\ncatch (e) {\n    if (/not found|already/i.test(e.message)) { sessionRevisions.delete(id); sessionRuntimeRevisions.delete(id); }\n    else showMessage(e.message);\n}","preventionTips":["Treat 'not found' replies as success and clear local caches.","Await waitForPendingSave(id) implicitly (remove() already does) before any delete UI feedback.","Disable the delete button immediately on click to prevent double-submit from two clients.","Always pass a non-empty session id."],"tags":["network","agent-session","typescript"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}