{"record":{"id":"864b2bb50710b34a","repo":"toeverything/AFFiNE","slug":"copilot-session-not-found","errorCode":"copilot_session_not_found","errorMessage":"Copilot session not found.","messagePattern":"Copilot session not found\\.","errorType":"error_code","errorClass":"CopilotSessionNotFound","httpStatus":404,"severity":"error","filePath":"packages/backend/server/src/models/copilot-session.ts","lineNumber":658,"sourceCode":"    options: UpdateChatSessionOptions,\n    internalCall = false\n  ): Promise<string> {\n    const { userId, sessionId, docId, promptName, pinned, title } = options;\n    const sanitizedTitle = this.sanitizeString(title);\n    const session = await this.getExists(\n      sessionId,\n      {\n        id: true,\n        workspaceId: true,\n        docId: true,\n        parentSessionId: true,\n        pinned: true,\n        promptAction: true,\n      },\n      { userId }\n    );\n    if (!session) {\n      throw new CopilotSessionNotFound();\n    }\n\n    // not allow to update action session\n    if (!internalCall) {\n      if (session.promptAction) {\n        throw new CopilotSessionInvalidInput(\n          `Cannot update action: ${session.id}`\n        );\n      } else if (docId && session.parentSessionId) {\n        throw new CopilotSessionInvalidInput(\n          `Cannot update docId for forked session: ${session.id}`\n        );\n      }\n    }\n\n    let nextPromptAction: string | null | undefined;\n    if (promptName) {\n      nextPromptAction = options.promptAction;","sourceCodeStart":640,"sourceCodeEnd":676,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/models/copilot-session.ts#L640-L676","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait sessionModel.update({ sessionId: staleId, userId, docId });\n// after\nconst live = await sessionModel.getExists(staleId, { id: true }, { userId });\nif (!live) throw new Error('session gone — reload list');\nawait sessionModel.update({ sessionId: live.id, userId, docId });","handlingStrategy":"validation","validationCode":"const live = await sessionModel.getExists(sessionId, { id: true }, { userId });\nif (!live) {\n  throw new Error('Session not found for this user — reload list');\n}\nawait sessionModel.update({ sessionId, userId, /* ... */ });","typeGuard":"const isOwnedSession = (s: { id: string } | null): s is { id: string } => s !== null;","tryCatchPattern":"try {\n  await sessionModel.update(payload);\n} catch (e) {\n  if (e instanceof UserFriendlyError && e.code === 'copilot_session_not_found') {\n    await refreshSessions();\n    return;\n  }\n  throw e;\n}","preventionTips":["Refresh the session list before letting the user edit.","Pass userId so getExists enforces ownership.","Treat soft-deleted sessions as gone (deletedAt:null filter)."],"tags":["copilot","session","update","not-found"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}