fullstackhero/dotnet-starter-kit · warning · NotFoundException
User not found.
Error message
User {userId} not found. What it means
DisableTwoFactorCommandHandler throws NotFoundException when UserManager.FindByIdAsync returns null for the id taken from the current user's claims. This means the JWT identifies a user that no longer exists in the identity database.
Solutions
- Re-authenticate to get a token bound to an existing user
- Verify the JWT's sub/nameidentifier claim matches a row inAspNetUsers (dotnet: SELECT "Id" FROM "AspNetUsers" WHERE "Id" = '<claim>')
- Confirm the API is connected to the intended database/environment
- If users are soft-deleted, decide whether FindByIdAsync should filter them and return a clearer message
Example fix
// before
var user = await _userManager.FindByIdAsync(userId)
?? throw new NotFoundException($"User {userId} not found.");
// after
if (await _userManager.FindByIdAsync(userId) is not User user)
{
_logger.LogWarning("Disable 2FA rejected: user {UserId} from token no longer exists", userId);
throw new UnauthorizedException("Session is no longer valid. Please sign in again.");
} Defensive patterns
Strategy: try-catch
Validate before calling
const sub = parseJwt(accessToken)?.sub; if (!sub) await reauthenticate();
Try / catch
try {
await api.disableTwoFactor(cmd);
} catch (e) {
if (e.status === 404 && /User .* not found/.test(e.message)) {
clearSession();
throw new SessionExpiredError();
}
throw e;
} Prevention
- Invalidate server-side sessions/tokens when a user is deleted
- Re-login after environment or database switches
- Don't reuse tokens across environments
When it happens
Trigger: Calling disable-2FA with a valid but stale token issued for a deleted user; a token whose nameidentifier claim points to a user in a different database/environment; test tokens with fabricated user ids.
Common situations: User account deleted or hard-removed while an access token was still valid; pointing the API at a fresh/migrated database while reusing old tokens; multi-environment token reuse (dev token against prod DB).
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/1c2df2fb50a6f930.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Identity/Modules.Identity/Features/v1/TwoFactor/Disable/DisableTwoFactorCommandHandler.cs:34
public DisableTwoFactorCommandHandler(UserManager<FshUser> userManager, ICurrentUser currentUser)
{
_userManager = userManager;
_currentUser = currentUser;
}
public async ValueTask<bool> Handle(
DisableTwoFactorCommand command, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(command);
if (!_currentUser.IsAuthenticated())
{
throw new UnauthorizedException();
}
var userId = _currentUser.GetUserId().ToString();
var user = await _userManager.FindByIdAsync(userId)
?? throw new NotFoundException($"User {userId} not found.");
// Require current password so a stolen access token alone can't downgrade
// account security.
if (!await _userManager.CheckPasswordAsync(user, command.CurrentPassword))
{
throw new UnauthorizedException("Current password is incorrect.");
}
await _userManager.SetTwoFactorEnabledAsync(user, false);
await _userManager.ResetAuthenticatorKeyAsync(user);
return true;
}
}
View on GitHub (pinned to 3f2959e683)