microsoft/aspire · critical · DistributedApplicationException
{result.ValidationMessage}
Error message
{result.ValidationMessage} What it means
AddDevTunnel validates the dev tunnel resource (including the local Dev Tunnels CLI version, via ValidateDevTunnelCliVersionAsync) before proceeding to login and tunnel creation. If validation fails, result.ValidationMessage describes the concrete problem and the builder throws a DistributedApplicationException to abort startup rather than creating a broken tunnel. It is a fail-fast guard so misconfigured tunnels or an unusable CLI are surfaced immediately.
Solutions
- Read the thrown ValidationMessage to see the exact validation failure it reports.
- Install or update the Dev Tunnels CLI per https://learn.microsoft.com/azure/developer/dev-tunnels/get-started#install and ensure 'devtunnel' resolves on PATH.
- Re-run the app host after the CLI fix; validation is re-executed on each start.
Example fix
// before (CLI missing/outdated => validation fails at startup)
var tunnel = builder.AddDevTunnel("my-tunnel").WithReference(api);
// after: install/update devtunnel first, e.g.
// winget install Microsoft.DevTunnels --version <latest>
// then verify: devtunnel --version
var tunnel = builder.AddDevTunnel("my-tunnel").WithReference(api); Defensive patterns
Strategy: validation
Validate before calling
// before starting the app host
var psi = new System.Diagnostics.ProcessStartInfo("devtunnel", "--version") { RedirectStandardOutput = true };
using var p = System.Diagnostics.Process.Start(psi)!;
var ver = p.StandardOutput.ReadToEnd();
if (string.IsNullOrWhiteSpace(ver)) throw new InvalidOperationException("Dev Tunnels CLI not installed/on PATH"); Prevention
- Install/update the devtunnel CLI as part of machine and CI image setup
- Verify 'devtunnel --version' works in the same shell/PATH used by the app host
- Pin a minimum CLI version in your team docs and check it in onboarding scripts
When it happens
Trigger: Calling AddDevTunnel/AddDevTunnelForPolyglot when commandValidator.ValidateAsync returns IsValid=false for the tunnel resource — typically because the Dev Tunnels CLI is not installed, is missing from PATH, or has an unsupported version detected by the CLI version validation callback.
Common situations: Fresh machines or CI agents where the Dev Tunnels CLI (devtunnel) was never installed; an outdated devtunnel version below the minimum supported by the package; CLI installed only for another user/shell so it is not on PATH.
Understand the failure class
Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.
Related errors
- Aspire skills bundle contains an empty relative path.
- Aspire skills bundle skill
- Aspire skills bundle version
- Cannot tunnel endpoint
- CLI path must be provided
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/514af094fd7a3a94.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.DevTunnels/DevTunnelResourceBuilderExtensions.cs:138
{
var logger = e.Services.GetRequiredService<ResourceLoggerService>().GetLogger(tunnelResource);
var eventing = e.Services.GetRequiredService<IDistributedApplicationEventing>();
var commandValidator = e.Services.GetRequiredService<IRequiredCommandValidator>();
var devTunnelEnvironmentManager = e.Services.GetRequiredService<DevTunnelLoginManager>();
var devTunnelClient = e.Services.GetRequiredService<IDevTunnelClient>();
// Validate the CLI is available and version is supported.
// We use manual validation here instead of WithRequiredCommand call because our
// OnBeforeResourceStarted handler runs before the global RequiredCommandValidationLifecycleHook runs.
var cliAnnotation = new RequiredCommandAnnotation(tunnelResource.Command)
{
HelpLink = "https://learn.microsoft.com/azure/developer/dev-tunnels/get-started#install",
ValidationCallback = ValidateDevTunnelCliVersionAsync
};
var result = await commandValidator.ValidateAsync(tunnelResource, cliAnnotation, ct).ConfigureAwait(false);
if (!result.IsValid)
{
throw new DistributedApplicationException(result.ValidationMessage);
}
// Login to the dev tunnels service if needed
logger.LogInformation("Ensuring user is logged in to dev tunnel service");
await devTunnelEnvironmentManager.EnsureUserLoggedInAsync(ct).ConfigureAwait(false);
// Create the dev tunnel
string resolvedTunnelId;
try
{
logger.LogInformation("Creating dev tunnel '{TunnelId}'", tunnelResource.TunnelId);
var tunnelStatus = await devTunnelClient.CreateTunnelAsync(tunnelResource.TunnelId, tunnelResource.Options, logger, ct).ConfigureAwait(false);
// The CLI resolves a bare ID and returns its cluster-qualified ID. Use that ID for
// port operations because bare IDs may not resolve tunnels across clusters.
// See https://github.com/microsoft/aspire/issues/18790.
resolvedTunnelId = tunnelStatus.TunnelId;
logger.LogDebug("Dev tunnel '{TunnelId}' created", tunnelResource.TunnelId);
}View on GitHub (pinned to 25830f84bd)