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

  1. Have the client tear down its realtime subscriptions and clear local state when its own user is deleted.
  2. Validate the userId exists before opening the realtime profile room.
  3. 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

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.