{"record":{"id":"1c2df2fb50a6f930","repo":"fullstackhero/dotnet-starter-kit","slug":"user-userid-not-found","errorCode":null,"errorMessage":"User {userId} not found.","messagePattern":"User (.+?) not found\\.","errorType":"exception","errorClass":"NotFoundException","httpStatus":404,"severity":"warning","filePath":"src/Modules/Identity/Modules.Identity/Features/v1/TwoFactor/Disable/DisableTwoFactorCommandHandler.cs","lineNumber":34,"sourceCode":"    public DisableTwoFactorCommandHandler(UserManager<FshUser> userManager, ICurrentUser currentUser)\n    {\n        _userManager = userManager;\n        _currentUser = currentUser;\n    }\n\n    public async ValueTask<bool> Handle(\n        DisableTwoFactorCommand command, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(command);\n\n        if (!_currentUser.IsAuthenticated())\n        {\n            throw new UnauthorizedException();\n        }\n\n        var userId = _currentUser.GetUserId().ToString();\n        var user = await _userManager.FindByIdAsync(userId)\n            ?? throw new NotFoundException($\"User {userId} not found.\");\n\n        // Require current password so a stolen access token alone can't downgrade\n        // account security.\n        if (!await _userManager.CheckPasswordAsync(user, command.CurrentPassword))\n        {\n            throw new UnauthorizedException(\"Current password is incorrect.\");\n        }\n\n        await _userManager.SetTwoFactorEnabledAsync(user, false);\n        await _userManager.ResetAuthenticatorKeyAsync(user);\n        return true;\n    }\n}\n","sourceCodeStart":16,"sourceCodeEnd":48,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Features/v1/TwoFactor/Disable/DisableTwoFactorCommandHandler.cs#L16-L48","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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"],"exampleFix":"// before\nvar user = await _userManager.FindByIdAsync(userId)\n    ?? throw new NotFoundException($\"User {userId} not found.\");\n// after\nif (await _userManager.FindByIdAsync(userId) is not User user)\n{\n    _logger.LogWarning(\"Disable 2FA rejected: user {UserId} from token no longer exists\", userId);\n    throw new UnauthorizedException(\"Session is no longer valid. Please sign in again.\");\n}","handlingStrategy":"try-catch","validationCode":"const sub = parseJwt(accessToken)?.sub;\nif (!sub) await reauthenticate();","typeGuard":null,"tryCatchPattern":"try {\n  await api.disableTwoFactor(cmd);\n} catch (e) {\n  if (e.status === 404 && /User .* not found/.test(e.message)) {\n    clearSession();\n    throw new SessionExpiredError();\n  }\n  throw e;\n}","preventionTips":["Invalidate server-side sessions/tokens when a user is deleted","Re-login after environment or database switches","Don't reuse tokens across environments"],"tags":["identity","user-not-found","jwt","two-factor"],"backgroundTag":"user-not-found","analyzedSha":"3f2959e683e9f83f13e55e1678c9119f63c7e8e5","analyzedAt":"2026-09-15T22:20:53.684Z","contentChangedAt":"2026-09-15T22:20:53.684Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}