fullstackhero/dotnet-starter-kit · error · UnauthorizedException
Tenant context is required.
Error message
Tenant context is required.
What it means
GenerateInvoicesCommandHandler throws UnauthorizedException when the caller has no tenant context (MultiTenantContext/TenantInfo is null). Platform-wide invoice generation is a root-operator action, so an anonymous or non-tenant-resolved caller is rejected first.
Solutions
- Send the tenant identifier (X-Tenant header or tenant host) with the request.
- Verify Finbuckle tenant resolution strategy and configured tenant mappings.
- For automation, use the root tenant context explicitly (MultitenancyConstants.Root.Id).
- If the endpoint should be non-tenant, move it to a root-scoped route with proper resolver config.
Example fix
// before
await http.PostAsync("/api/v1/invoices/generate", content); // no tenant header
// after
request.Headers.Add("X-Tenant", "root");
await http.SendAsync(request); Defensive patterns
Strategy: validation
Validate before calling
var tenantId = tenantAccessor.MultiTenantContext?.TenantInfo?.Id; if (string.IsNullOrEmpty(tenantId)) throw new UnauthorizedException("Tenant context is required."); Try / catch
try { await api.GenerateInvoices(cmd); } catch (UnauthorizedAccessException) { // attach tenant header and retry once } Prevention
- Always send the tenant header on API clients
- Configure tenant resolution for custom domains
- Give background jobs an explicit tenant (root) context
When it happens
Trigger: Calling the generate-invoices endpoint without a valid tenant header/host so Finbuckle cannot resolve TenantInfo; calling from a background job or CLI without tenant context configured.
Common situations: Missing X-Tenant header in API calls; misconfigured tenant resolver for custom domains; invoking the endpoint from a Hangfire job or service client that doesn't set tenant context.
Related errors
- Only the root operator may generate invoices across tenants.
- Tenant context is required.
- Tenant context is required.
- Tenant context is required.
- Tenant context is required.
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/4335a71febfb52cb.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Billing/Modules.Billing/Features/v1/Invoices/GenerateInvoices/GenerateInvoicesCommandHandler.cs:22
using FSH.Modules.Billing.Contracts.v1.Invoices;
using FSH.Modules.Billing.Services;
using Mediator;
namespace FSH.Modules.Billing.Features.v1.Invoices.GenerateInvoices;
public sealed class GenerateInvoicesCommandHandler(
IBillingService billing,
IMultiTenantContextAccessor<AppTenantInfo> tenantAccessor)
: ICommandHandler<GenerateInvoicesCommand, int>
{
public async ValueTask<int> Handle(GenerateInvoicesCommand command, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(command);
// Platform-wide invoice generation runs across EVERY tenant — it is a root-operator action.
// A tenant admin (who also holds Billing.Manage) must not be able to trigger it.
var callerTenantId = tenantAccessor.MultiTenantContext?.TenantInfo?.Id
?? throw new UnauthorizedException("Tenant context is required.");
if (callerTenantId != MultitenancyConstants.Root.Id)
{
throw new ForbiddenException("Only the root operator may generate invoices across tenants.");
}
return await billing.GenerateInvoicesForAllTenantsAsync(command.PeriodYear, command.PeriodMonth, cancellationToken).ConfigureAwait(false);
}
}
View on GitHub (pinned to 3f2959e683)