{"record":{"id":"f716ce9adc550376","repo":"toeverything/AFFiNE","slug":"user-not-found","errorCode":"user_not_found","errorMessage":"User not found.","messagePattern":"User not found\\.","errorType":"exception","errorClass":"UserNotFound","httpStatus":404,"severity":"error","filePath":"packages/backend/server/src/core/user/realtime.ts","lineNumber":110,"sourceCode":"    });\n  }\n\n  @OnEvent('user.settings.updated', { suppressError: true })\n  onUserSettingsUpdated({ userId }: Events['user.settings.updated']) {\n    this.publisher?.publishChanged(\n      'user.settings.changed',\n      {},\n      'settings-updated',\n      { room: realtimeUserSettingsRoom(userId) }\n    );\n  }\n\n  private async getProfile(\n    userId: string\n  ): Promise<CurrentUserProfileSnapshot> {\n    const user = await this.models.user.get(userId);\n    if (!user) {\n      throw new UserNotFound();\n    }\n    const current = sessionUser(user);\n    return {\n      id: current.id,\n      name: current.name,\n      email: current.email,\n      emailVerified: current.emailVerified,\n      hasPassword: current.hasPassword,\n      avatarUrl: current.avatarUrl ?? null,\n      features: (\n        await this.models.userFeature.list(\n          userId,\n          undefined,\n          Array.from(this.availableUserFeatures())\n        )\n      )\n        .filter(feature => this.availableUserFeatures().has(feature))\n        .map(feature => this.serializeFeature(feature)),","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/core/user/realtime.ts#L92-L128","documentation":"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.","triggerScenarios":"Realtime profile subscription for a userId that has been deleted or never existed, while the caller is otherwise authenticated.","commonSituations":"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.","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."],"exampleFix":"// before\nsubscribe(realtimeUserProfileRoom(cachedUserId));\n\n// after\nconst user = await fetchCurrentUser();\nif (user) subscribe(realtimeUserProfileRoom(user.id));\nelse redirectToLogin();","handlingStrategy":"validation","validationCode":"const me = await fetchCurrentUser();\nif (!me) { redirectToLogin(); return; }","typeGuard":"function userExists(user?: { id: string } | null): user is { id: string } {\n  return Boolean(user?.id);\n}","tryCatchPattern":"try {\n  await subscribe(realtimeUserProfileRoom(userId));\n} catch (e) {\n  if (e?.code === 'user_not_found') { teardownUserData(); redirectToLogin(); return; }\n  throw e;\n}","preventionTips":["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."],"tags":["user","realtime","not-found","database"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}