fullstackhero/dotnet-starter-kit · warning · CustomException

cannot impersonate yourself

Error message

cannot impersonate yourself

What it means

Self-impersonation (same user id and same tenant) is rejected with CustomException('cannot impersonate yourself', BadRequest) — an explicit 4xx rather than the 500 CustomException defaults to. It is pointless and would pollute the audit trail.

Solutions

  1. Pass a different TargetUserId (the user to impersonate)
  2. Fix the UI/form so the target user cannot default to the signed-in user
  3. Adjust tests to use two distinct users

Example fix

// before
const targetUserId = currentUser.id; // wrong: self
// after
const targetUserId = selectedUser.id; // a different user in the same tenant
Defensive patterns

Strategy: validation

Validate before calling

if (targetUserId === currentUserId && targetTenantId === currentTenantId) {
  throw new Error('cannot impersonate yourself: pick a different target user');
}

Try / catch

try { await api.startImpersonation(req); }
catch (e) { if (e.status === 400 && /yourself/.test(e.message)) { notify('Select a different user to impersonate'); return; } throw e; }

Prevention

When it happens

Trigger: Calling start-impersonation with TargetUserId equal to the caller's own user id and TargetTenantId equal to the caller's tenant.

Common situations: A support UI pre-filling the 'current user' as the target by accident; automated tests using the same fixture user for actor and target; copying your own user id from the profile page into the request.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at src/Modules/Identity/Modules.Identity/Features/v1/Impersonation/StartImpersonation/StartImpersonationCommandHandler.cs:77

        var actorUserId = _currentUser.GetUserId().ToString();
        var actorTenantId = _currentUser.GetTenant()
            ?? throw new UnauthorizedException("missing tenant context");
        var actorUserName = _currentUser.Name;

        // Cross-tenant impersonation requires the actor to be in the root tenant. Tenant admins
        // can only impersonate users within their own tenant.
        if (!string.Equals(actorTenantId, MultitenancyConstants.Root.Id, StringComparison.Ordinal)
            && !string.Equals(actorTenantId, request.TargetTenantId, StringComparison.Ordinal))
        {
            throw new ForbiddenException("cross-tenant impersonation is restricted to platform operators");
        }

        // Prevent self-impersonation (pointless, confuses the audit trail). Caller error → explicit 4xx,
        // not the 500 CustomException defaults to.
        if (string.Equals(actorUserId, request.TargetUserId, StringComparison.Ordinal)
            && string.Equals(actorTenantId, request.TargetTenantId, StringComparison.Ordinal))
        {
            throw new CustomException("cannot impersonate yourself", errors: null, System.Net.HttpStatusCode.BadRequest);
        }

        // Prevent nesting: if the caller is already impersonating, require end-impersonation first.
        var callerClaims = _currentUser.GetUserClaims();
        if (callerClaims is not null
            && callerClaims.Any(c => c.Type == ClaimConstants.ActorSubject))
        {
            throw new CustomException(
                "end current impersonation before starting a new one",
                errors: null,
                System.Net.HttpStatusCode.BadRequest);
        }

        var targetClaimsResult = await _identityService
            .BuildClaimsForUserAsync(request.TargetUserId, request.TargetTenantId, cancellationToken);

        if (targetClaimsResult is null)
        {

View on GitHub (pinned to 3f2959e683)