OrchardCMS/OrchardCore · error · InvalidOperationException

Failed to retrieve the current endpoint route builder.

Error message

Failed to retrieve the current endpoint route builder.

What it means

When composing a shell's request pipeline, Orchard Core calls UseRouting and then reads the IEndpointRouteBuilder stored by the routing middleware in builder.Properties (key 'EndpointRouteBuilder') so module routes can be configured outside UseEndpoints. If the property is absent or holds a different object, endpoint routing never ran or was replaced, and this InvalidOperationException is thrown.

Solutions

  1. Ensure UseRouting() is called on the application builder before the shell pipeline is composed.
  2. Do not remove or overwrite the 'EndpointRouteBuilder' entry in builder.Properties.
  3. Check custom IStartup filter ordering so endpoint routing middleware runs before module route configuration.
  4. If using custom pipeline branching, compose the shell on the main pipeline where routing is active.

Example fix

// before
app.UseStaticFiles();
app.UseOrchardCore(); // shell pipeline expects routing state

// after
app.UseStaticFiles();
app.UseRouting();
app.UseOrchardCore();
Defensive patterns

Strategy: try-catch

Validate before calling

// Before composing the shell pipeline:
if (!builder.Properties.TryGetValue("EndpointRouteBuilder", out var obj) || obj is not IEndpointRouteBuilder)
    throw new InvalidOperationException("Call app.UseRouting() before UseOrchardCore().");

Type guard

bool HasEndpointRouteBuilder(IApplicationBuilder builder) =>
    builder.Properties.TryGetValue("__EndpointRouteBuilder", out var obj) && obj is IEndpointRouteBuilder;

Try / catch

try { await builder.ConfigurePipelineAsync(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("endpoint route builder"))
{
    _logger.LogCritical(ex, "Endpoint routing middleware missing; check UseRouting placement");
    throw;
}

Prevention

When it happens

Trigger: Calling ConfigurePipelineAsync (via UseOrchardCore/BuildPipelineInternalAsync) on an IApplicationBuilder where UseRouting() was not invoked before, or where a custom middleware/branch replaced the EndpointRouteBuilder entry, or where EndpointRoutingMiddleware was disabled (UseRouting called after this code or globally disabled).

Common situations: Custom Startup/patch code removes or reorders UseRouting; a third-party middleware framework clears builder.Properties; running with a minimal host that skips endpoint routing; misordered pipeline configuration during shell rebuild.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13). Data as JSON: /api/errors/f931ce7ec0ee007d. Report an issue: GitHub.

Appendix: source

Thrown at src/OrchardCore/OrchardCore/Modules/Extensions/ShellPipelineExtensions.cs:84

    }

    /// <summary>
    /// Configures the tenant pipeline.
    /// </summary>
    private static async ValueTask ConfigurePipelineAsync(ApplicationBuilder builder)
    {
        // 'IStartup' instances are ordered by module dependencies with a 'ConfigureOrder' of 0 by default.
        // 'OrderBy' performs a stable sort, so the order is preserved among equal 'ConfigureOrder' values.
        var startups = builder.ApplicationServices.GetServices<IStartup>().OrderBy(s => s.ConfigureOrder);

        // Should be done first.
        builder.UseRouting();

        // Try to retrieve the current 'IEndpointRouteBuilder'.
        if (!builder.Properties.TryGetValue(EndpointRouteBuilder, out var obj) ||
            obj is not IEndpointRouteBuilder routes)
        {
            throw new InvalidOperationException("Failed to retrieve the current endpoint route builder.");
        }

        // Routes can be then configured outside 'UseEndpoints()'.
        var services = ShellScope.Services;
        foreach (var startup in startups)
        {
            if (startup is IAsyncStartup asyncStartup)
            {
                await asyncStartup.ConfigureAsync(builder, routes, services);
            }

            startup.Configure(builder, routes, services);
        }

        // Knowing that routes are already configured.
        builder.UseEndpoints(routes => { });
    }
}

View on GitHub (pinned to 4306c0717f)