{"record":{"id":"093acd350bc44abd","repo":"dotnet/orleans","slug":"orleans-dashboard-services-have-not-been-registere","errorCode":null,"errorMessage":"Orleans Dashboard services have not been registered. Please call AddDashboard on ISiloBuilder or IClientBuilder.","messagePattern":"Orleans Dashboard services have not been registered\\. Please call AddDashboard on ISiloBuilder or IClientBuilder\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Dashboard/Orleans.Dashboard/ServiceCollectionExtensions.cs","lineNumber":98,"sourceCode":"    /// <example>\n    /// <code>\n    /// // Basic usage\n    /// app.MapOrleansDashboard();\n    ///\n    /// // With authentication\n    /// app.MapOrleansDashboard().RequireAuthorization();\n    ///\n    /// // With custom base path\n    /// app.MapOrleansDashboard(routePrefix: \"/dashboard\");\n    /// </code>\n    /// </example>\n    public static RouteGroupBuilder MapOrleansDashboard(\n        this IEndpointRouteBuilder endpoints,\n        [StringSyntax(\"Route\")] string? routePrefix = null)\n    {\n        // Create static assets provider\n        var assets = endpoints.ServiceProvider.GetService<EmbeddedAssetProvider>()\n            ?? throw new InvalidOperationException(\"Orleans Dashboard services have not been registered. \" +\n                \"Please call AddDashboard on ISiloBuilder or IClientBuilder.\");\n\n        // Create a route group for all dashboard endpoints\n        var group = endpoints.MapGroup(routePrefix ?? \"\");\n\n        // Static assets - these match the paths referenced in the built CSS/HTML\n        // When a routePrefix is specified, redirect requests without trailing slash to include it.\n        // This ensures relative asset paths (like index.min.js) resolve correctly.\n        group.MapGet(\"/\", (HttpContext ctx) =>\n        {\n            if (!string.IsNullOrEmpty(routePrefix) && ctx.Request.Path.Value?.EndsWith('/') == false)\n            {\n                // Redirect to the same path with a trailing slash, preserving the query string\n                var redirectUrl = $\"{ctx.Request.PathBase}{ctx.Request.Path}/{ctx.Request.QueryString}\";\n                return Results.Redirect(redirectUrl, permanent: true);\n            }\n            return assets.ServeAsset(\"index.html\", ctx);\n        });","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/dotnet/orleans/blob/fca799fa70ecb6ad975224271703ca43221f58de/src/Dashboard/Orleans.Dashboard/ServiceCollectionExtensions.cs#L80-L116","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\napp.MapOrleansDashboard(); // throws: provider not registered\n\n// after\nbuilder.Host.ConfigureContainer<ISiloBuilder>(b => b.AddDashboard());\napp.MapOrleansDashboard();","handlingStrategy":"validation","validationCode":"if (services.All(s => s.ServiceType != typeof(EmbeddedAssetProvider)))\n    throw new InvalidOperationException(\"Call builder.AddDashboard() first\");\napp.MapOrleansDashboard();","typeGuard":"static bool DashboardRegistered(IServiceCollection s) =>\n    s.Any(x => x.ServiceType == typeof(EmbeddedAssetProvider));","tryCatchPattern":"try { app.MapOrleansDashboard(); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"AddDashboard\"))\n{ logger.LogCritical(ex, \"Dashboard services missing; call AddDashboard\"); throw; }","preventionTips":["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"],"tags":["dashboard","di","registration","startup"],"backgroundTag":null,"analyzedSha":"fca799fa70ecb6ad975224271703ca43221f58de","analyzedAt":"2026-08-13T19:55:57.938Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}