fullstackhero/dotnet-starter-kit · error · NotFoundException

User was not found.

Error message

User {userId} was not found.

What it means

Thrown as a NotFoundException by AdminConfirmEmailAsync when no user with the given userId exists in the current tenant's user set. The service queries userManager.Users filtered by Id and tenant, so a wrong ID, a cross-tenant ID, or an already-deleted user produces this error. It is a deliberate 404-style signal that the confirmation target does not exist.

Solutions

  1. Verify the userId exists via a user list/search endpoint before confirming
  2. Check that the request is hitting the correct tenant (tenant header/claim resolves the expected tenant)
  3. Confirm the user was not deleted (check the users table including soft-deleted rows if applicable)
  4. Regenerate/refresh the admin user list so the client isn't holding a stale ID

Example fix

// before
await userRegistrationService.AdminConfirmEmailAsync(userId, ct);
// after
var exists = await userManager.Users.AnyAsync(u => u.Id == userId, ct);
if (!exists) return Results.NotFound($"User {userId} does not exist in this tenant.");
await userRegistrationService.AdminConfirmEmailAsync(userId, ct);
Defensive patterns

Strategy: validation

Validate before calling

bool userExists = await userManager.Users.AnyAsync(u => u.Id == userId, cancellationToken);

Type guard

if (userId == Guid.Empty) throw new ArgumentException("userId must be a non-empty Guid");

Try / catch

catch (NotFoundException ex) { return Results.NotFound(new { ex.Message }); }

Prevention

When it happens

Trigger: Calling AdminConfirmEmailAsync with a userId that does not exist, was deleted, or belongs to a different tenant (the query runs under the tenant filter of BaseDbContext).

Common situations: Admin UI passes a stale row ID after the user was deleted; client sends a Guid from another tenant database; typo'd or truncated ID from a copy-paste; tests using random Guids without seeding users.

Understand the failure class

Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.

Related errors


AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15). Data as JSON: /api/errors/0dcd9f902001ac37. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Identity/Modules.Identity/Services/UserRegistrationService.cs:99

        _ = user ?? throw new CustomException("An error occurred while confirming E-Mail.");

        code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));
        var result = await userManager.ConfirmEmailAsync(user, code);

        return result.Succeeded
            ? string.Format(CultureInfo.InvariantCulture, "Account Confirmed for E-Mail {0}. You can now use the /api/tokens endpoint to generate JWT.", user.Email)
            : throw new CustomException(string.Format(CultureInfo.InvariantCulture, "An error occurred while confirming {0}", user.Email));
    }

    public async Task AdminConfirmEmailAsync(string userId, CancellationToken cancellationToken = default)
    {
        EnsureValidTenant();

        var user = await userManager.Users
            .Where(u => u.Id == userId)
            .FirstOrDefaultAsync(cancellationToken)
            ?? throw new NotFoundException($"User {userId} was not found.");

        // Idempotent: a second confirm is a no-op rather than an error.
        if (user.EmailConfirmed)
        {
            return;
        }

        user.EmailConfirmed = true;
        var result = await userManager.UpdateAsync(user);
        if (!result.Succeeded)
        {
            throw new CustomException(string.Format(
                CultureInfo.InvariantCulture,
                "An error occurred while confirming the email for {0}: {1}",
                user.Email,
                string.Join("; ", result.Errors.Select(e => e.Description))));
        }
    }

View on GitHub (pinned to 3f2959e683)