toeverything/AFFiNE · error · UserNotFound
user_not_found
user_not_found
Error message
User not found.
What it means
Thrown by the getInviteInfo query when there is no invitee user id to render: the invite record has no inviteeUserId (or it is empty) and the caller is not signed in, so inviteeId = inviteeUserId || user?.id resolves to undefined. This is the anonymous-visitor-on-a-targeted-invite case. Code user_not_found, type resource_not_found, HTTP 404.
Solutions
- Require sign-in before fetching invite info: route the user through authentication when no session exists, then retry getInviteInfo.
- In the client, check for an authenticated session before rendering the invite page and show a 'sign in to view this invitation' state.
- If the invite should be open to anyone, use an invite link (isLink) rather than a targeted email invitation.
Example fix
// before
const info = await getInviteInfo(inviteId); // anonymous + targeted invite -> user_not_found
// after
if (!(await session.currentUser())) {
return redirect(`/sign-in?redirect_uri=${encodeURIComponent(location.pathname)}`);
}
const info = await getInviteInfo(inviteId); Defensive patterns
Strategy: validation
Validate before calling
// require a session before fetching targeted invite info
const user = await session.currentUser();
if (!user) {
redirect(`/sign-in?redirect_uri=${encodeURIComponent(currentPath)}`);
}
await getInviteInfo(inviteId); Type guard
function isUserNotFound(e: unknown): boolean {
const ext = (e as { extensions?: Record<string, unknown> })?.extensions;
return String(ext?.name ?? '').toLowerCase() === 'user_not_found';
} Try / catch
try {
await getInviteInfo(inviteId);
} catch (e) {
if (isUserNotFound(e) && !currentUser) {
await signInThenReturn(); // retry after authentication
} else throw e;
} Prevention
- Treat invite pages as authenticated routes when invitations are targeted.
- Keep auth sessions alive on long-lived invite tabs (silent refresh).
When it happens
Trigger: Opening an invite page (query getInviteInfo(inviteId)) where the invitation was created for a specific user, but the request is unauthenticated (no current user) — e.g. the recipient's link opened in a logged-out browser or incognito window, or an expired auth session dropped the CurrentUser.
Common situations: Invite preview pages rendered server-side or in incognito; auth token missing/expired so @CurrentUser is undefined; invitation links shared with a different person than the targeted invitee.
Understand the failure class
Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.
Related errors
AI-assisted analysis of toeverything/AFFiNE@2af30773ae (2026-08-18).
Data as JSON: /api/errors/1dc2ed01df6cbb12.
Report an issue: GitHub.
Appendix: source
Thrown at packages/backend/server/src/core/workspaces/resolvers/member.ts:497
return true;
}
@Throttle('strict')
@Query(() => InvitationType, {
description: 'get workspace invitation info',
})
async getInviteInfo(
@CurrentUser() user: UserType,
@Args('inviteId') inviteId: string
): Promise<InvitationType> {
const { workspaceId, inviteeUserId, isLink } =
await this.workspaceService.getInviteInfo(inviteId);
if (!isLink && user.id !== inviteeUserId) {
throw new InvitationAccountMismatch();
}
const workspace = await this.workspaceService.getWorkspaceInfo(workspaceId);
const owner = await this.models.workspaceUser.getOwner(workspaceId);
const inviteeId = inviteeUserId || user.id;
const invitee = await this.models.user.getWorkspaceUser(inviteeId);
if (!invitee) throw new UserNotFound();
let status: WorkspaceMemberStatus | undefined;
if (isLink) {
const invitation = await this.models.workspaceUser.get(
workspaceId,
inviteeId
);
status = invitation?.status;
} else {
const invitation = await this.models.workspaceUser.getById(inviteId);
status = invitation?.status;
}
View on GitHub (pinned to 2af30773ae)