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

  1. Refresh the session list client-side and retry with the current sessionId.
  2. Confirm ownership: ensure userId matches the session's owner before update.
  3. If soft-deleted, restore (clear deletedAt) or create a new session instead of updating.
  4. 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

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


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/864b2bb50710b34a. Report an issue: GitHub.