{"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":"exception","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/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/backend/server/src/models/copilot-session.ts#L640-L676","documentation":"CopilotSessionNotFound thrown at the top of CopilotSessionModel.update() (copilot-session.ts:645-659). getExists(sessionId, select, { userId }) queries aiSession with the caller's userId and deletedAt: null, returning null when the id does not exist, belongs to a different user, or was soft-deleted; update() converts that null into this error before touching any field. The internalCall flag only relaxes later guards (action/fork rules), not this existence check.","triggerScenarios":"update({ userId, sessionId, ... }) with an unknown, foreign, or soft-deleted sessionId - wrong/expired id, session owned by another user, or a session removed by cleanup.","commonSituations":"Stale sessionId persisted in localStorage after the session was cleaned up; another tab or device deleted the chat; sending an id from a different user's account during testing.","solutions":["Re-resolve the session for the current user (e.g. via has(sessionId, userId)) and use a live id","If the session was deleted, create a new session instead of updating","Verify the authenticated userId matches the session owner"],"exampleFix":"// before\nawait sessions.update({ userId, sessionId: staleId, title });\n\n// after\nlet sessionId = staleId;\nif (!(await sessions.has(sessionId, userId))) {\n  sessionId = await sessions.create({ userId, workspaceId, docId });\n}\nawait sessions.update({ userId, sessionId, title });","handlingStrategy":"validation","validationCode":"let sessionId = requestedId;\nif (!(await sessions.has(sessionId, userId))) {\n  sessionId = await sessions.create({ userId, workspaceId, docId });\n}\nawait sessions.update({ userId, sessionId, ...patch });","typeGuard":null,"tryCatchPattern":"try {\n  await sessions.update(options);\n} catch (e) {\n  if (e instanceof CopilotSessionNotFound) {\n    // re-resolve or create a session; surface 'conversation expired' to the client\n  }\n  throw e;\n}","preventionTips":["Always scope session lookups by the authenticated userId","Refresh stale session ids from the server before mutations","Handle not-found as a UX state (start a new chat), not an error page"],"tags":["copilot","session","not-found","ownership"],"backgroundTag":"session-not-found","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}