toeverything/AFFiNE · error · CopilotSessionNotFound
copilot_session_not_found
copilot_session_not_found
Error message
Copilot session not found.
What it means
CopilotSessionNotFound (resource_not_found / copilot_session_not_found) thrown inside CopilotSessionModel.update (packages/backend/server/src/models/copilot-session.ts:658). update() first calls getExists(sessionId, {...}, { userId }) to load the session scoped to the caller. If no row matches (wrong id, wrong user, hard-deleted, or already soft-deleted because getExists filters deletedAt: null), the guard throws before any field is mutated.
Source
Thrown at packages/backend/server/src/models/copilot-session.ts:658
options: UpdateChatSessionOptions,
internalCall = false
): Promise<string> {
const { userId, sessionId, docId, promptName, pinned, title } = options;
const sanitizedTitle = this.sanitizeString(title);
const session = await this.getExists(
sessionId,
{
id: true,
workspaceId: true,
docId: true,
parentSessionId: true,
pinned: true,
promptAction: true,
},
{ userId }
);
if (!session) {
throw new CopilotSessionNotFound();
}
// not allow to update action session
if (!internalCall) {
if (session.promptAction) {
throw new CopilotSessionInvalidInput(
`Cannot update action: ${session.id}`
);
} else if (docId && session.parentSessionId) {
throw new CopilotSessionInvalidInput(
`Cannot update docId for forked session: ${session.id}`
);
}
}
let nextPromptAction: string | null | undefined;
if (promptName) {
nextPromptAction = options.promptAction;View on GitHub (pinned to 26c515e050)
Solutions
- Refresh the session list client-side and retry with the current sessionId.
- Confirm ownership: ensure userId matches the session's owner before update.
- If soft-deleted, restore (clear deletedAt) or create a new session instead of updating.
- Catch by code copilot_session_not_found and show 'Session no longer available' in the UI.
Example fix
// before
await sessionModel.update({ sessionId: staleId, userId, docId });
// after
const live = await sessionModel.getExists(staleId, { id: true }, { userId });
if (!live) throw new Error('session gone — reload list');
await sessionModel.update({ sessionId: live.id, userId, docId }); Defensive patterns
Strategy: validation
Validate before calling
const live = await sessionModel.getExists(sessionId, { id: true }, { userId });
if (!live) {
throw new Error('Session not found for this user — reload list');
}
await sessionModel.update({ sessionId, userId, /* ... */ }); Type guard
const isOwnedSession = (s: { id: string } | null): s is { id: string } => s !== null; Try / catch
try {
await sessionModel.update(payload);
} catch (e) {
if (e instanceof UserFriendlyError && e.code === 'copilot_session_not_found') {
await refreshSessions();
return;
}
throw e;
} Prevention
- Refresh the session list before letting the user edit.
- Pass userId so getExists enforces ownership.
- Treat soft-deleted sessions as gone (deletedAt:null filter).
When it happens
Trigger: Calling update({ sessionId, userId, ... }) where sessionId does not belong to userId, does not exist, or has been soft-deleted. getExists adds id + deletedAt:null + userId to the Prisma where clause, so any mismatch yields null.
Common situations: Stale sessionId in the client after the session was deleted; cross-user access attempt; sessionId typo; race with a concurrent delete; trying to update a forked session whose parent was removed.
Related errors
- Document ${docId} not found
- copilot_prompt_invalid
- copilot_session_deleted
- copilot_session_invalid_input
- authentication_required
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/864b2bb50710b34a.
Report an issue: GitHub.