{"record":{"id":"0dcd9f902001ac37","repo":"fullstackhero/dotnet-starter-kit","slug":"user-userid-was-not-found","errorCode":null,"errorMessage":"User {userId} was not found.","messagePattern":"User (.+?) was not found\\.","errorType":"exception","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"src/Modules/Identity/Modules.Identity/Services/UserRegistrationService.cs","lineNumber":99,"sourceCode":"\n        _ = user ?? throw new CustomException(\"An error occurred while confirming E-Mail.\");\n\n        code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));\n        var result = await userManager.ConfirmEmailAsync(user, code);\n\n        return result.Succeeded\n            ? string.Format(CultureInfo.InvariantCulture, \"Account Confirmed for E-Mail {0}. You can now use the /api/tokens endpoint to generate JWT.\", user.Email)\n            : throw new CustomException(string.Format(CultureInfo.InvariantCulture, \"An error occurred while confirming {0}\", user.Email));\n    }\n\n    public async Task AdminConfirmEmailAsync(string userId, CancellationToken cancellationToken = default)\n    {\n        EnsureValidTenant();\n\n        var user = await userManager.Users\n            .Where(u => u.Id == userId)\n            .FirstOrDefaultAsync(cancellationToken)\n            ?? throw new NotFoundException($\"User {userId} was not found.\");\n\n        // Idempotent: a second confirm is a no-op rather than an error.\n        if (user.EmailConfirmed)\n        {\n            return;\n        }\n\n        user.EmailConfirmed = true;\n        var result = await userManager.UpdateAsync(user);\n        if (!result.Succeeded)\n        {\n            throw new CustomException(string.Format(\n                CultureInfo.InvariantCulture,\n                \"An error occurred while confirming the email for {0}: {1}\",\n                user.Email,\n                string.Join(\"; \", result.Errors.Select(e => e.Description))));\n        }\n    }","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Services/UserRegistrationService.cs#L81-L117","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Verify the userId exists via a user list/search endpoint before confirming","Check that the request is hitting the correct tenant (tenant header/claim resolves the expected tenant)","Confirm the user was not deleted (check the users table including soft-deleted rows if applicable)","Regenerate/refresh the admin user list so the client isn't holding a stale ID"],"exampleFix":"// before\nawait userRegistrationService.AdminConfirmEmailAsync(userId, ct);\n// after\nvar exists = await userManager.Users.AnyAsync(u => u.Id == userId, ct);\nif (!exists) return Results.NotFound($\"User {userId} does not exist in this tenant.\");\nawait userRegistrationService.AdminConfirmEmailAsync(userId, ct);","handlingStrategy":"validation","validationCode":"bool userExists = await userManager.Users.AnyAsync(u => u.Id == userId, cancellationToken);","typeGuard":"if (userId == Guid.Empty) throw new ArgumentException(\"userId must be a non-empty Guid\");","tryCatchPattern":"catch (NotFoundException ex) { return Results.NotFound(new { ex.Message }); }","preventionTips":["Always resolve userId from a freshly fetched user list, never long-lived client state","Include tenant resolution in integration tests so cross-tenant IDs fail loudly","Log the resolved tenant id alongside userId for diagnosis"],"tags":["identity","user-not-found","multitenancy"],"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"}