toeverything/AFFiNE · error · UserNotFound
user_not_found
user_not_found
Error message
User not found.
What it means
Thrown by UserRealtimeProvider.getProfile() when models.user.get(userId) returns null. This builds the profile snapshot for the realtime profile room; if the user id does not correspond to a row in the database, the snapshot cannot be produced.
Source
Thrown at packages/backend/server/src/core/user/realtime.ts:110
});
}
@OnEvent('user.settings.updated', { suppressError: true })
onUserSettingsUpdated({ userId }: Events['user.settings.updated']) {
this.publisher?.publishChanged(
'user.settings.changed',
{},
'settings-updated',
{ room: realtimeUserSettingsRoom(userId) }
);
}
private async getProfile(
userId: string
): Promise<CurrentUserProfileSnapshot> {
const user = await this.models.user.get(userId);
if (!user) {
throw new UserNotFound();
}
const current = sessionUser(user);
return {
id: current.id,
name: current.name,
email: current.email,
emailVerified: current.emailVerified,
hasPassword: current.hasPassword,
avatarUrl: current.avatarUrl ?? null,
features: (
await this.models.userFeature.list(
userId,
undefined,
Array.from(this.availableUserFeatures())
)
)
.filter(feature => this.availableUserFeatures().has(feature))
.map(feature => this.serializeFeature(feature)),View on GitHub (pinned to 26c515e050)
Solutions
- Have the client tear down its realtime subscriptions and clear local state when its own user is deleted.
- Validate the userId exists before opening the realtime profile room.
- If the id is stale, force re-authentication to obtain the current user.
Example fix
// before subscribe(realtimeUserProfileRoom(cachedUserId)); // after const user = await fetchCurrentUser(); if (user) subscribe(realtimeUserProfileRoom(user.id)); else redirectToLogin();
Defensive patterns
Strategy: validation
Validate before calling
const me = await fetchCurrentUser();
if (!me) { redirectToLogin(); return; } Type guard
function userExists(user?: { id: string } | null): user is { id: string } {
return Boolean(user?.id);
} Try / catch
try {
await subscribe(realtimeUserProfileRoom(userId));
} catch (e) {
if (e?.code === 'user_not_found') { teardownUserData(); redirectToLogin(); return; }
throw e;
} Prevention
- Validate the user still exists before subscribing to their profile room.
- Tear down local state for deleted accounts.
- Re-authenticate to obtain a current userId when in doubt.
When it happens
Trigger: Realtime profile subscription for a userId that has been deleted or never existed, while the caller is otherwise authenticated.
Common situations: User was deleted (deleteAccount/deleteUser) but a client still holds the id and subscribes; id propagated from a stale token after account deletion; test fixture referencing a non-existent user.
Related errors
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/f716ce9adc550376.
Report an issue: GitHub.