{"record":{"id":"746d10729c52550f","repo":"toeverything/AFFiNE","slug":"user-not-found-746d10","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/resolver.ts","lineNumber":121,"sourceCode":"  })\n  @Public()\n  async getPublicUserById(\n    @Args('id', { type: () => String }) id: string\n  ): Promise<PublicUserType | null> {\n    return await this.models.user.getPublicUser(id);\n  }\n\n  @Mutation(() => UserType, {\n    name: 'uploadAvatar',\n    description: 'Upload user avatar',\n  })\n  async uploadAvatar(\n    @CurrentUser() user: CurrentUser,\n    @Args({ name: 'avatar', type: () => GraphQLUpload })\n    avatar: FileUpload\n  ) {\n    if (!user) {\n      throw new UserNotFound();\n    }\n\n    const avatarBuffer = await readBufferWithLimit(\n      avatar.createReadStream(),\n      5 * OneMB\n    );\n    const contentType = sniffMime(avatarBuffer, avatar.mimetype)?.toLowerCase();\n    if (!contentType || !contentType.startsWith('image/')) {\n      throw new ImageFormatNotSupported({ format: contentType || 'unknown' });\n    }\n\n    let processedAvatarBuffer: Buffer;\n    try {\n      processedAvatarBuffer = await processImage(avatarBuffer, 512, false);\n    } catch {\n      throw new ImageFormatNotSupported({ format: contentType });\n    }\n","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/2af30773aecd567f09b346e7b72fc69143144057/packages/backend/server/src/core/user/resolver.ts#L103-L139","documentation":"The uploadAvatar GraphQL mutation requires a current user; when the request is unauthenticated (or the session expired so @CurrentUser resolves to null) it throws user_not_found rather than an authentication error.","triggerScenarios":"Calling the uploadAvatar mutation without a session cookie/access token; an expired session at upload time; GraphQL clients dropping the auth header on multipart uploads.","commonSituations":"Avatar upload attempted after token expiry; multipart GraphQL proxying that strips cookies; scripts calling the mutation directly without credentials.","solutions":["Sign in and retry the upload with the session attached","Ensure your GraphQL client sends credentials (cookies/authorization) on multipart avatar uploads","Handle USER_NOT_FOUND from this mutation as a prompt to re-authenticate"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// ensure a session before uploading\nif (!session.user) {\n  await redirectToSignIn();\n  return;\n}\nawait uploadAvatar(file);","typeGuard":"function isCurrentUser(user?: CurrentUser | null): user is CurrentUser {\n  return !!user?.id;\n}","tryCatchPattern":"try {\n  await uploadAvatar(file);\n} catch (e) {\n  if (e?.extensions?.code === 'USER_NOT_FOUND') await reauthenticateAndRetry();\n  else throw e;\n}","preventionTips":["Send credentials on multipart GraphQL uploads (cookies/authorization header)","Check auth state before enabling the avatar upload control","Interpret USER_NOT_FOUND from account mutations as an expired session signal"],"tags":["graphql","authentication","avatar","upload"],"backgroundTag":"user-not-found","analyzedSha":"2af30773aecd567f09b346e7b72fc69143144057","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}