fullstackhero/dotnet-starter-kit · error · UnauthorizedException

invalid tenant

Error message

invalid tenant

What it means

Thrown by UserStatusService.EnsureValidTenant when the Finbuckle multitenant context has no resolved tenant id for the current request. Session-related operations (create/list/revoke sessions, admin and tenant session queries) all require a tenant context to scope their queries.

Solutions

  1. Send the correct tenant identifier with the request (header/query/host per the configured Finbuckle strategy).
  2. Check the multitenancy middleware/strategy registration in the API host.
  3. Verify the tenant exists and its resolver mappings (host, identifier) are correct.
  4. In tests, set up the tenant context before calling session services.

Example fix

// before: request without tenant info -> 401 invalid tenant
await apiFetch("/api/v1/users/sessions", { headers: {} });
// after
await apiFetch("/api/v1/users/sessions", { headers: { "tenant": "root" } });
Defensive patterns

Strategy: validation

Validate before calling

const tenantId = resolveTenant(); // header/query/host per app config
if (!tenantId) throw new Error("No tenant resolved — set the tenant header before calling session APIs");

Try / catch

try { await apiFetch("/api/v1/users/sessions"); } catch (e) {
  if (e.status === 401 && /invalid tenant/i.test(e.message)) { redirectTenantSelection(); return; }
  throw e;
}

Prevention

When it happens

Trigger: Any session endpoint hit without a tenant identifier header/segment, or with middleware configured so the tenant resolver cannot match the request (wrong host, missing __tenant__ form/query value).

Common situations: Calling the API directly without the tenant header that the frontend normally injects; misconfigured multitenancy strategy in the host; integration tests missing tenant setup; new custom domain not registered with the tenant resolver.

Related errors


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

Appendix: source

Thrown at src/Modules/Identity/Modules.Identity/Services/UserStatusService.cs:43

    public async Task ToggleStatusAsync(bool activateUser, string userId, CancellationToken cancellationToken)
    {
        EnsureValidTenant();

        var context = await BuildToggleContextAsync(userId, activateUser, cancellationToken);

        await ValidateTogglePermissionsAsync(context, cancellationToken);

        ApplyStatusChange(context);

        await SaveAndAuditAsync(context, cancellationToken);
    }

    private void EnsureValidTenant()
    {
        if (string.IsNullOrWhiteSpace(multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id))
        {
            throw new UnauthorizedException("invalid tenant");
        }
    }

    private async Task<ToggleStatusContext> BuildToggleContextAsync(
        string userId,
        bool activateUser,
        CancellationToken cancellationToken)
    {
        var actorId = currentUser.GetUserId();
        if (actorId == Guid.Empty)
        {
            throw new UnauthorizedException("authenticated user required to toggle status");
        }

        var actor = await userManager.FindByIdAsync(actorId.ToString())
            ?? throw new UnauthorizedException("current user not found");

        var targetUser = await userManager.Users

View on GitHub (pinned to 3f2959e683)