dotnet/orleans · error · InvalidOperationException
Orleans Dashboard services have not been registered. Please
Error message
Orleans Dashboard services have not been registered. Please call AddDashboard on ISiloBuilder or IClientBuilder.
What it means
InvalidOperationException from MapOrleansDashboard (the endpoint-routing extension) when EmbeddedAssetProvider is not registered in the DI container. The dashboard's endpoint mapper resolves the asset provider to serve static files; if AddDashboard was never called on the silo/client builder, the service is absent and the extension throws at endpoint configuration.
Source
Thrown at src/Dashboard/Orleans.Dashboard/ServiceCollectionExtensions.cs:98
/// <example>
/// <code>
/// // Basic usage
/// app.MapOrleansDashboard();
///
/// // With authentication
/// app.MapOrleansDashboard().RequireAuthorization();
///
/// // With custom base path
/// app.MapOrleansDashboard(routePrefix: "/dashboard");
/// </code>
/// </example>
public static RouteGroupBuilder MapOrleansDashboard(
this IEndpointRouteBuilder endpoints,
[StringSyntax("Route")] string? routePrefix = null)
{
// Create static assets provider
var assets = endpoints.ServiceProvider.GetService<EmbeddedAssetProvider>()
?? throw new InvalidOperationException("Orleans Dashboard services have not been registered. " +
"Please call AddDashboard on ISiloBuilder or IClientBuilder.");
// Create a route group for all dashboard endpoints
var group = endpoints.MapGroup(routePrefix ?? "");
// Static assets - these match the paths referenced in the built CSS/HTML
// When a routePrefix is specified, redirect requests without trailing slash to include it.
// This ensures relative asset paths (like index.min.js) resolve correctly.
group.MapGet("/", (HttpContext ctx) =>
{
if (!string.IsNullOrEmpty(routePrefix) && ctx.Request.Path.Value?.EndsWith('/') == false)
{
// Redirect to the same path with a trailing slash, preserving the query string
var redirectUrl = $"{ctx.Request.PathBase}{ctx.Request.Path}/{ctx.Request.QueryString}";
return Results.Redirect(redirectUrl, permanent: true);
}
return assets.ServeAsset("index.html", ctx);
});View on GitHub (pinned to fca799fa70)
Solutions
- Call builder.AddDashboard() (ISiloBuilder or IClientBuilder) before calling app.MapOrleansDashboard().
- Ensure AddDashboard is registered in the same composition root whose ServiceProvider the endpoint route resolves.
- If conditionally registering, guard MapOrleansDashboard with the same condition.
Example fix
// before app.MapOrleansDashboard(); // throws: provider not registered // after builder.Host.ConfigureContainer<ISiloBuilder>(b => b.AddDashboard()); app.MapOrleansDashboard();
Defensive patterns
Strategy: validation
Validate before calling
if (services.All(s => s.ServiceType != typeof(EmbeddedAssetProvider)))
throw new InvalidOperationException("Call builder.AddDashboard() first");
app.MapOrleansDashboard(); Type guard
static bool DashboardRegistered(IServiceCollection s) =>
s.Any(x => x.ServiceType == typeof(EmbeddedAssetProvider)); Try / catch
try { app.MapOrleansDashboard(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("AddDashboard"))
{ logger.LogCritical(ex, "Dashboard services missing; call AddDashboard"); throw; } Prevention
- Always pair MapOrleansDashboard with AddDashboard in the same composition root
- Register AddDashboard unconditionally if you map the endpoints unconditionally
- Resolve the asset provider from the same ServiceProvider the endpoints use
When it happens
Trigger: Calling app.MapOrleansDashboard() (or the minimal-API route group) without first calling builder.AddDashboard(...) on the ISiloBuilder or IClientBuilder. The check happens when the endpoint route is built, i.e. usually at Program.cs startup.
Common situations: Copying the MapOrleansDashboard() line from docs without the corresponding AddDashboard(...) registration, or registering the dashboard on the client but mapping endpoints against the silo's service provider (or vice versa). Also when AddDashboard is conditionally registered (e.g. only in Development) but MapOrleansDashboard runs in all environments.
Related errors
- Journal format key '{journalFormatKey}' requires keyed servi
- Journal format key '{journalFormatKey}' resolved format '{jo
- Embedded resource not found: {fullResourceName}
- WEBSITE_PRIVATE_PORTS must contain at least one TCP port.
- The required setting '{name}' isn't configured.
AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13).
Data as JSON: /api/errors/093acd350bc44abd.
Report an issue: GitHub.