{"record":{"id":"12bcca37f97ed192","repo":"toeverything/AFFiNE","slug":"session-not-found","errorCode":null,"errorMessage":"Session not found","messagePattern":"Session not found","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"packages/backend/server/src/plugins/copilot/conversation/inbox.ts","lineNumber":45,"sourceCode":"};\n\n@Injectable()\nexport class ConversationInboxService {\n  constructor(\n    private readonly chatSession: ChatSessionService,\n    private readonly ac: PermissionAccess,\n    private readonly models: Models,\n    private readonly storage: CopilotStorage,\n    private readonly submissions: CompatSubmissionStore\n  ) {}\n\n  async createMessage(\n    userId: string,\n    options: CreateInboxMessage\n  ): Promise<string> {\n    const session = await this.chatSession.get(options.sessionId);\n    if (!session || session.config.userId !== userId) {\n      throw new BadRequestException('Session not found');\n    }\n\n    const attachments: PromptMessage['attachments'] = options.attachments || [];\n    const blobs = await Promise.all(\n      options.blob ? [options.blob] : options.blobs || []\n    );\n\n    const focusSelectors = options.params?.focusSelectors;\n    const hasWorkspaceContext =\n      attachments.length > 0 ||\n      blobs.length > 0 ||\n      (Array.isArray(options.params?.scopeSelectors) &&\n        options.params.scopeSelectors.length > 0) ||\n      (Array.isArray(options.params?.preferredSourceIds) &&\n        options.params.preferredSourceIds.length > 0) ||\n      (focusSelectors === undefined\n        ? session.config.focus.selectors.length > 0\n        : Array.isArray(focusSelectors) && focusSelectors.length > 0);","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/backend/server/src/plugins/copilot/conversation/inbox.ts#L27-L63","documentation":"Thrown by CopilotInbox.createMessage when the referenced chat session does not exist, or exists but belongs to a different user (session.config.userId !== userId). It is a BadRequestException (HTTP 400) rather than 404, so the client sees 'Session not found' on a malformed or cross-user request. The userId check doubles as an authorization guard: you can never post into someone else's session even with a valid sessionId.","triggerScenarios":"Calling the copilot inbox/createMessage API with a sessionId that was deleted, never existed, or was truncated/typo'd; calling with a sessionId owned by another user account; using a stale sessionId after the user switched accounts or the session was garbage-collected.","commonSituations":"Frontend keeps a cached sessionId after the session list refreshed; test harness copies a sessionId from a different seeded user; session expired and was purged while the composer stayed open.","solutions":["Verify the sessionId came from the same user's copilot chats/sessions query and was not modified","Re-fetch the user's session list and confirm the id still exists before retrying","If the session was deleted, create a new session and repost the message","As a maintainer: catch BadRequestException with message 'Session not found' and surface a 'start a new chat' action in the UI instead of a raw error"],"exampleFix":"// before\nawait inbox.createMessage(userId, { sessionId: staleId, content });\n\n// after\nconst session = await chatSession.get(staleId);\nif (!session || session.config.userId !== userId) {\n  // re-create or pick a fresh session instead of posting\n  throw new Error('Session is gone, start a new chat');\n}\nawait inbox.createMessage(userId, { sessionId: staleId, content });","handlingStrategy":"validation","validationCode":"const session = await chatSession.get(sessionId);\nif (!session || session.config.userId !== currentUserId) {\n  throw new Error('Session unavailable — refetch session list');\n}","typeGuard":"const isOwnSession = (\n  s: { config: { userId: string } } | null | undefined,\n  userId: string\n): s is { config: { userId: string } } => !!s && s.config.userId === userId;","tryCatchPattern":"try {\n  await inbox.createMessage(userId, options);\n} catch (e) {\n  if (e instanceof BadRequestException && e.message === 'Session not found') {\n    await refreshSessions(); // drop stale id, pick or create a new session\n  } else throw e;\n}","preventionTips":["Always source sessionIds from a fresh session-list query for the current user","Invalidate cached session ids on account switch or session deletion events","Treat 'Session not found' as a signal to resync, not to blind-retry"],"tags":["copilot","session","authorization","bad-request"],"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-14T05:17:10.506Z"}