siyuan-note/siyuan · error · Error
Failed to remove agent session
Error message
Failed to remove agent session
What it means
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.
Source
Thrown at app/src/layout/dock/agent/SessionStore.ts:203
}
return {revision, session: resp.data?.session};
};
const save = previous ? previous.then((result) => persist(result.revision)) : persist(baseRevision);
sessionSaveQueues.set(snapshot.id, save);
try {
return await save;
} finally {
if (sessionSaveQueues.get(snapshot.id) === save) {
sessionSaveQueues.delete(snapshot.id);
}
}
},
async remove(id: string): Promise<void> {
await waitForPendingSave(id);
const resp = await fetchSyncPost(API + "/removeSession", {id}, APP_HEADER) as {code: number; msg?: string};
if (!resp || resp.code !== 0) {
throw new Error(resp?.msg || "Failed to remove agent session");
}
sessionRevisions.delete(id);
sessionRuntimeRevisions.delete(id);
},
async rename(id: string, newTitle: string): Promise<void> {
const session = await this.load(id);
if (!session) { return; }
session.title = newTitle;
await this.save(session);
},
async setPermission(id: string, permissionMode: AgentPermissionMode): Promise<AgentPermissionMode> {
await waitForPendingSave(id);
const resp = await fetchSyncPost(API + "/setPermission", {
sessionID: id,
permissionMode,
}, APP_HEADER) as {code: number; msg?: string; data?: {permissionMode?: AgentPermissionMode}};View on GitHub (pinned to 251596fc0d)
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.
Example fix
// before
await SessionStore.remove(id);
// after
try {
await SessionStore.remove(id);
} catch (e) {
if (/not found|already/i.test(e.message)) { sessionRevisions.delete(id); sessionRuntimeRevisions.delete(id); }
else { showMessage(e.message); throw e; }
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!id || typeof id !== 'string') throw new Error('remove: id required');
const exists = (await SessionStore.list({keyword: id})).sessions.some(s => s.id === id); Type guard
function isSessionId(v: unknown): v is string { return typeof v === 'string' && v.length > 0; } Try / catch
try { await SessionStore.remove(id); }
catch (e) {
if (/not found|already/i.test(e.message)) { sessionRevisions.delete(id); sessionRuntimeRevisions.delete(id); }
else showMessage(e.message);
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Failed to save agent session
- Failed to update agent session permission
- ${data.error.message}
- Canvas 2D is unavailable
- WebGL2 is unavailable
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/b6b6a16b1322158b.
Report an issue: GitHub.