toeverything/AFFiNE · error · NotFoundException

Session not found

Error message

Session not found

What it means

NotFoundException('Session not found') from the copilot.session GraphQL field: after assertPermission passes, chatSession.getMetaState(sessionId) returned no state — the session id does not exist (or its projection state was never built/purged). Distinct from the ownership check in inbox.ts: here permission was already established for the workspace/doc, and the failure is purely data lookup.

Source

Thrown at packages/backend/server/src/plugins/copilot/resolver.ts:443

        .allowLocal()
        .assert('Workspace.Copilot');
    }
    return { userId: user.id, workspaceId, docId: docId || undefined };
  }

  @ResolveField(() => CopilotSessionType, {
    description: 'Get the session by id',
    complexity: 2,
  })
  async session(
    @Parent() copilot: CopilotType,
    @CurrentUser() user: CurrentUser,
    @Args('sessionId') sessionId: string
  ): Promise<CopilotSessionType> {
    await this.assertPermission(user, copilot);
    const state = await this.chatSession.getMetaState(sessionId);
    if (!state) {
      throw new NotFoundException('Session not found');
    }

    const projected = this.historyProjector.projectSession(state, {
      requestUserId: user.id,
      skipVisibilityFilter: true,
    });
    if (!projected) {
      throw new NotFoundException('Session not found');
    }

    return this.transformToSessionType(projected);
  }

  @ResolveField(() => [CopilotSessionType], {
    description: 'Get the session list in the workspace',
    deprecationReason: 'use `chats` instead',
    complexity: 2,
  })

View on GitHub (pinned to b4c8548c09)

Solutions

  1. Re-query the session list and drop ids that 404, then let the user pick a live session
  2. For deep links, catch the NotFoundException and redirect to the session list
  3. Verify the sessionId string is passed unchanged (no trimming/encoding artifacts)

Example fix

# before
query { copilot { session(sessionId: $staleId) { id messages } } }

# after
# client: on SESSION_NOT_FOUND error, refetch list and navigate
try {
  const s = await fetchSession(sessionId);
} catch (e) {
  if (e.code === 'NOT_FOUND') router.replace('/copilot/chats');
}
Defensive patterns

Strategy: try-catch

Validate before calling

const state = await chatSession.getMetaState(sessionId);
if (!state) {
  await refreshSessionList();
  throw new Error('Session no longer exists');
}

Try / catch

try {
  return await fetchCopilotSession(copilot, sessionId);
} catch (e) {
  if (e instanceof NotFoundException && /Session not found/.test(e.message)) {
    removeSessionFromCache(sessionId);
    navigateTo('/copilot/chats');
  } else throw e;
}

Prevention

When it happens

Trigger: Querying copilot session with a deleted/never-existing sessionId; session state evicted by retention/GC; id copied with extra characters; race where the session list showed an entry that was deleted before the detail query.

Common situations: Stale session list in the UI after deletion in another tab; deep link to a removed session; migration dropped old session state.

Related errors


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/133bd08e016b44e3. Report an issue: GitHub.