microsoft/aspire · error · InvalidOperationException

The MAUI OTLP dev tunnel configuration was not initialized…

Error message

The MAUI OTLP dev tunnel configuration was not initialized before tunnel startup.

What it means

WithOtlpDevTunnel defers tunnel setup via closures that capture a tunnelConfig reference initialized later. If the BeforeResourceStartedEvent handler for the dev tunnel fires while tunnelConfig is still null, the captured state was never initialized, so the subscriber throws InvalidOperationException rather than configuring a tunnel blindly.

Solutions

  1. Ensure WithOtlpDevTunnel is applied to the MAUI resource before the AppHost starts, so initialization runs before the tunnel's BeforeResourceStartedEvent.
  2. Verify the tunnel resource name matches the one created by WithOtlpDevTunnel (tunnelName) — starting a foreign tunnel resource triggers the guard.
  3. Don't manually start or reorder the DevTunnelResource; let the MAUI extension manage its lifecycle.
  4. If reproducing in tests, wait for model construction to finish before triggering resource-start events.

Example fix

// before
var maui = builder.AddMauiProject(...);
builder.AddDevTunnel("otlp-tunnel").WithOtlpDevTunnel(maui); // tunnel created separately, wrong order
// after
maui.WithOtlpDevTunnel(); // extension creates and wires the tunnel itself
Defensive patterns

Strategy: validation

Validate before calling

// assert wiring happens before start
if (appModelBuilt) throw new InvalidOperationException("WithOtlpDevTunnel must be applied before builder.Build()/Run().");

Try / catch

try { await startAsync(); } catch (InvalidOperationException ex) when (ex.Message.Contains("tunnel configuration was not initialized")) { /* move WithOtlpDevTunnel earlier in app-model construction */ }

Prevention

When it happens

Trigger: The BeforeResourceStartedEvent for the DevTunnelResource named tunnelName fires before WithOtlpDevTunnel's initialization path ran (tunnelConfig still null) at src/Aspire.Hosting.Maui/MauiOtlpExtensions.cs:132.

Common situations: Starting the dev tunnel resource before the MAUI OTLP wiring completes; calling WithOtlpDevTunnel on the wrong resource or after the app model started; event ordering changes across Aspire versions; manually starting the tunnel resource in tests.

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 microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/f15660b3e6a429b6. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.Maui/MauiOtlpExtensions.cs:132

        var stubBuilder = appBuilder.AddResource(stubResource)
            .ExcludeFromManifest();

        // Hide the stub from the dashboard UI
        stubBuilder.WithHidden().WithInitialState(new CustomResourceSnapshot
        {
            ResourceType = "OtlpStub",
            Properties = []
        });

        if (configuredOtlpEndpoint is null)
        {
            appBuilder.Eventing.Subscribe<BeforeResourceStartedEvent>(async (evt, ct) =>
            {
                if (evt.Resource is DevTunnelResource devTunnelResource &&
                    string.Equals(devTunnelResource.Name, tunnelName, StringComparisons.ResourceName))
                {
                    var currentTunnelConfig = tunnelConfig ?? throw new InvalidOperationException("The MAUI OTLP dev tunnel configuration was not initialized before tunnel startup.");
                    if (currentTunnelConfig.IsOtlpEndpointResolved)
                    {
                        return;
                    }

                    var dashboardResource = appBuilder.Resources.FirstOrDefault(resource =>
                        string.Equals(resource.Name, KnownResourceNames.AspireDashboard, StringComparisons.ResourceName));
                    if (dashboardResource is null)
                    {
                        var exception = new DistributedApplicationException($"The MAUI OTLP dev tunnel for resource '{parentBuilder.Resource.Name}' requires the Aspire dashboard to be enabled or an explicit OTLP endpoint URL to be configured.");
                        if (currentTunnelConfig.TryFailOtlpEndpointResolution(exception))
                        {
                            throw exception;
                        }

                        return;
                    }

View on GitHub (pinned to 25830f84bd)