microsoft/aspire · error · DistributedApplicationException
Failed to get devtunnel CLI version. Output
Error message
Failed to get devtunnel CLI version. Output: '{output}'. Error: '{error}' What it means
DevTunnelCliClient.GetVersionAsync parses the output of 'devtunnel --version'. If the parsed output does not yield a version and the CLI produced any output or error text, the client throws this DistributedApplicationException including the raw output and error so the underlying CLI failure is visible.
Solutions
- Run 'devtunnel --version' manually and compare its output with what the client expects; upgrade or reinstall the CLI to a supported version
- Run 'devtunnel user login' if the error text indicates authentication failure
- Remove PATH shims/wrappers that add extra text to the CLI output
- Check the message's Output/Error fields for the exact CLI failure before retrying
Example fix
// before
var version = await client.GetVersionAsync(); // throws when CLI output unparseable
// after
var (stdout, stderr) = await CaptureAsync(() => new DevTunnelCli(path).GetVersionAsync());
logger.LogInformation("devtunnel version output: {Out} / {Err}", stdout, stderr); // diagnose first
var version = await client.GetVersionAsync(); Defensive patterns
Strategy: try-catch
Validate before calling
var probe = new StringBuilder(); var errs = new StringBuilder(); await cli.GetVersionAsync(probe, errs); // if probe output is empty or unparseable, fix the CLI before resource startup depends on it
Try / catch
try
{
var version = await client.GetVersionAsync();
}
catch (DistributedApplicationException ex)
{
logger.LogError(ex, "devtunnel CLI version probe failed; check CLI install, login state, and version format.");
throw; // fail fast: later tunnel operations will fail too
} Prevention
- Keep the devtunnel CLI updated to a version the client supports
- Run 'devtunnel user login' once per machine before starting the AppHost
- Avoid PATH shims/wrappers that inject extra output into CLI stdout/stderr
- Verify raw 'devtunnel --version' output after any CLI upgrade
When it happens
Trigger: Calling GetVersionAsync (directly or via DevTunnels resource startup) when the devtunnel CLI fails to produce a parseable version string — non-zero exit, empty output with error text, or unexpected output format from a changed CLI version.
Common situations: devtunnel CLI not logged in and prints an auth error; an old or preview devtunnel version whose --version output format differs; shell wrapper or PATH shim injecting extra output; CLI missing so errorWriter holds 'command not found'.
Related errors
- CLI path must be provided
- Could not parse Helm version from 'helm version --short'…
- Failed to delete port
- Failed to get access details for
- Failed to get user login status. Exit code
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/4d1763f3d6459f69.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.DevTunnels/DevTunnelCliClient.cs:63
.FirstOrDefault(l => l.StartsWith(prefix, StringComparison.OrdinalIgnoreCase));
var versionString = versionLine?.Length > prefix.Length
? versionLine[prefix.Length..].Trim()
: output;
// Trim the commit SHA suffix if present
if (versionString.IndexOf('+') is >= 0 and var plusIndex)
{
versionString = versionString[..plusIndex];
}
if (Version.TryParse(versionString, out var version))
{
return version;
}
}
var error = errorWriter.ToString().Trim();
throw new DistributedApplicationException($"Failed to get devtunnel CLI version. Output: '{output}'. Error: '{error}'");
}
public async Task<DevTunnelStatus> CreateTunnelAsync(string tunnelId, DevTunnelOptions options, ILogger? logger = default, CancellationToken cancellationToken = default)
{
var attempts = 0;
var exitCode = 0;
string? error = null;
string resolvedTunnelId = options.Region is not null ? $"{tunnelId}.{options.RegionCode}" : tunnelId;
while (attempts < _maxCliAttempts)
{
logger?.LogTrace("Creating dev tunnel '{TunnelId}' with options: {Options}", tunnelId, options.ToLoggerString());
if (attempts++ > 1)
{
logger?.LogTrace("Attempt {Attempt} of {MaxAttempts} to create dev tunnel '{TunnelId}'", attempts, _maxCliAttempts, tunnelId);
}
(var tunnel, exitCode, error) = await CallCliAsJsonAsync<DevTunnelStatus>((stdout, stderr, log, ct) => _cli.CreateTunnelAsync(tunnelId, options, stdout, stderr, log, ct),
"tunnel",View on GitHub (pinned to 25830f84bd)