toeverything/AFFiNE · error · UserNotFound

user_not_found

user_not_found

Error message

User not found.

What it means

Thrown by the uploadAvatar GraphQL mutation when the @CurrentUser() decorator resolves to a falsy value. This is a defensive auth check inside the resolver body — the mutation is reachable yet no authenticated user was attached, so there is no owner to attach the avatar to. (The same code shape repeats in removeAvatar.)

Source

Thrown at packages/backend/server/src/core/user/resolver.ts:119

  })
  @Public()
  async getPublicUserById(
    @Args('id', { type: () => String }) id: string
  ): Promise<PublicUserType | null> {
    return await this.models.user.getPublicUser(id);
  }

  @Mutation(() => UserType, {
    name: 'uploadAvatar',
    description: 'Upload user avatar',
  })
  async uploadAvatar(
    @CurrentUser() user: CurrentUser,
    @Args({ name: 'avatar', type: () => GraphQLUpload })
    avatar: FileUpload
  ) {
    if (!user) {
      throw new UserNotFound();
    }

    const avatarBuffer = await readBufferWithLimit(
      avatar.createReadStream(),
      5 * OneMB
    );
    const contentType = sniffMime(avatarBuffer, avatar.mimetype)?.toLowerCase();
    if (!contentType || !contentType.startsWith('image/')) {
      throw new ImageFormatNotSupported({ format: contentType || 'unknown' });
    }

    let processedAvatarBuffer: Buffer;
    try {
      processedAvatarBuffer = await processImage(avatarBuffer, 512, false);
    } catch {
      throw new ImageFormatNotSupported({ format: contentType });
    }

View on GitHub (pinned to 26c515e050)

Solutions

  1. Ensure the client sends a valid auth token with the uploadAvatar mutation.
  2. Re-authenticate on token expiry before retrying the avatar upload.
  3. Verify the GraphQL auth guard resolves CurrentUser for this mutation.

Example fix

// before — uploading after token expiry
await apollo.mutate({ mutation: UPLOAD_AVATAR, variables: { avatar } });

// after — refresh session first
await refreshSessionIfNeeded();
await apollo.mutate({ mutation: UPLOAD_AVATAR, variables: { avatar } });
Defensive patterns

Strategy: validation

Validate before calling

if (!currentUser) { await refreshSession(); return; }
await uploadAvatar(avatar);

Type guard

function hasCurrentUser(user?: CurrentUser | null): user is CurrentUser {
  return Boolean(user?.id);
}

Try / catch

try {
  await apollo.mutate({ mutation: UPLOAD_AVATAR, variables: { avatar } });
} catch (e) {
  if (e?.code === 'user_not_found' || e?.code === 'authentication_required') { await refreshSession(); return; }
  throw e;
}

Prevention

When it happens

Trigger: The uploadAvatar mutation is invoked without a valid authenticated session, so the CurrentUser argument is null/undefined.

Common situations: Session token expired before the mutation was sent; GraphQL operation issued before login completed; auth guard misconfigured to allow the mutation through without resolving a user.

Related errors


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/746d10729c52550f. Report an issue: GitHub.