{"record":{"id":"14da1e23626fcebe","repo":"fullstackhero/dotnet-starter-kit","slug":"target-user-not-found","errorCode":null,"errorMessage":"target user not found","messagePattern":"target user not found","errorType":"exception","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"src/Modules/Identity/Modules.Identity/Features/v1/Impersonation/StartImpersonation/StartImpersonationCommandHandler.cs","lineNumber":96,"sourceCode":"        }\n\n        // Prevent nesting: if the caller is already impersonating, require end-impersonation first.\n        var callerClaims = _currentUser.GetUserClaims();\n        if (callerClaims is not null\n            && callerClaims.Any(c => c.Type == ClaimConstants.ActorSubject))\n        {\n            throw new CustomException(\n                \"end current impersonation before starting a new one\",\n                errors: null,\n                System.Net.HttpStatusCode.BadRequest);\n        }\n\n        var targetClaimsResult = await _identityService\n            .BuildClaimsForUserAsync(request.TargetUserId, request.TargetTenantId, cancellationToken);\n\n        if (targetClaimsResult is null)\n        {\n            throw new NotFoundException(\"target user not found\");\n        }\n\n        var (subject, claims) = targetClaimsResult.Value;\n        var targetUserName = claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value\n            ?? claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Name)?.Value;\n\n        // Strip the auto-generated jti from BuildClaimsForUserAsync and inject our own, so the persisted\n        // ImpersonationGrant row and the issued JWT share the same jti.\n        var jti = Guid.NewGuid().ToString(\"N\");\n        var impersonationClaims = claims\n            .Where(c => c.Type != JwtRegisteredClaimNames.Jti)\n            .Concat(\n            [\n                new Claim(JwtRegisteredClaimNames.Jti, jti),\n                // RFC 8693 actor claims so the issued token carries who is acting.\n                new Claim(ClaimConstants.ActorSubject, actorUserId),\n                new Claim(ClaimConstants.ActorTenant, actorTenantId)\n            ])","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Features/v1/Impersonation/StartImpersonation/StartImpersonationCommandHandler.cs#L78-L114","documentation":"The handler asks IIdentityService.BuildClaimsForUserAsync to build claims for the target user in the target tenant. A null result means no such user exists in that tenant, so it throws NotFoundException('target user not found') (HTTP 404).","triggerScenarios":"TargetUserId does not exist, exists in a different tenant than TargetTenantId, or the user is inactive/deleted in that tenant.","commonSituations":"Target user id from another environment; targeting a user by email instead of id; tenant mismatch — user exists but under a different tenant id; user deleted while the support UI list was open.","solutions":["Verify TargetUserId and TargetTenantId match an existing, active user (re-fetch the user list for that tenant)","Confirm you are passing the user's id, not email or username","Check the target tenant id spelling against the tenants list"],"exampleFix":"// before\nawait api.startImpersonation({ targetUserId: user.email, targetTenantId }); // 404\n// after\nawait api.startImpersonation({ targetUserId: user.id, targetTenantId });","handlingStrategy":"validation","validationCode":"const user = await api.getUser(targetUserId, targetTenantId).catch(() => null);\nif (!user) throw new Error(`no user ${targetUserId} in tenant ${targetTenantId}`);","typeGuard":null,"tryCatchPattern":"try { await api.startImpersonation(req); }\ncatch (e) { if (e.status === 404 && /target user/.test(e.message)) { refreshTargetUserList(); return; } throw e; }","preventionTips":["Resolve targets from the tenant-scoped user list, not free-text input","Pass user ids, not emails/usernames","Re-fetch the user when the target list is stale","Verify the tenant id belongs to the same environment"],"tags":["not-found","user","impersonation"],"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"}