microsoft/aspire · error · DistributedApplicationException
Failed to get user login status. Exit code
Error message
Failed to get user login status. Exit code {exitCode}: {error} What it means
GetUserLoginStatusAsync invokes `devtunnel user status` and deserializes the login state. A non-zero CLI exit means no status object was returned, so this DistributedApplicationException is thrown including the exit code and CLI error text.
Solutions
- Run `devtunnel user login` interactively to establish credentials, then retry
- Verify the devtunnel CLI is installed and on PATH (`devtunnel --version`)
- Inspect the exit code and error text appended to the message for the root cause
- In CI, provision devtunnel credentials (e.g. via `devtunnel user login -g` with a token) before the app host starts
Example fix
// before
var status = await client.GetUserLoginStatusAsync(); // throws when logged out
// after
try
{
var status = await client.GetUserLoginStatusAsync();
}
catch (DistributedApplicationException ex)
{
logger.LogWarning(ex, "devtunnel CLI could not report login status; prompting login");
} Defensive patterns
Strategy: try-catch
Validate before calling
var which = Environment.GetEnvironmentVariable("PATH");
// ensure devtunnel CLI present:
var cliPath = Environment.GetEnvironmentVariable("PATH")!.Split(':').Select(d => Path.Combine(d, "devtunnel")).FirstOrDefault(File.Exists);
if (cliPath is null) throw new InvalidOperationException("devtunnel CLI not installed"); Type guard
static bool IsKnownLoginStatus(UserLoginStatus? s) => s is not null;
Try / catch
try { return await client.GetUserLoginStatusAsync(logger, ct); }
catch (DistributedApplicationException ex) { logger.LogWarning(ex, "Not logged in to dev tunnels"); return null; } Prevention
- Install the devtunnel CLI and run `devtunnel user login` before starting the app host
- In CI, provision non-interactive credentials
- Check `devtunnel user status` manually when diagnosing
When it happens
Trigger: Calling GetUserLoginStatusAsync (directly or via UserLoginAsync) when the devtunnel CLI is missing/broken, the user is not authenticated, or the CLI fails with an error instead of emitting status JSON.
Common situations: Fresh machines where `devtunnel user login` was never run; expired tokens; CI containers without the devtunnel CLI on PATH; proxy/network failures reaching the tunnel service.
Related errors
- CLI path must be provided
- Failed to delete port
- Failed to get access details for
- Failed to get devtunnel CLI version. Output
- Failed to parse JSON output into type
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/5526bbc83984201e.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.DevTunnels/DevTunnelCliClient.cs:268
return result ?? throw new DistributedApplicationException($"Failed to delete port '{portNumber}' on dev tunnel '{tunnelId}'. Exit code {exitCode}: {error}");
}
public async Task<DevTunnelAccessStatus> GetAccessAsync(string tunnelId, int? portNumber = null, ILogger? logger = default, CancellationToken cancellationToken = default)
{
logger?.LogTrace("Getting access details for {PortInfo}dev tunnel '{TunnelId}'.", portNumber.HasValue ? $"port '{portNumber}' on " : string.Empty, tunnelId);
var (access, exitCode, error) = await CallCliAsJsonAsync<DevTunnelAccessStatus>(
(stdout, stderr, log, ct) => _cli.ListAccessAsync(tunnelId, portNumber, stdout, stderr, log, ct),
logger, cancellationToken).ConfigureAwait(false);
return access ?? throw new DistributedApplicationException($"Failed to get access details for '{tunnelId}'{(portNumber.HasValue ? $" port {portNumber}" : "")}. Exit code {exitCode}: {error}");
}
public async Task<UserLoginStatus> GetUserLoginStatusAsync(ILogger? logger = default, CancellationToken cancellationToken = default)
{
logger?.LogTrace("Getting dev tunnel user login status.");
var (login, exitCode, error) = await CallCliAsJsonAsync<UserLoginStatus>(
_cli.UserStatusAsync,
logger, cancellationToken).ConfigureAwait(false);
return login ?? throw new DistributedApplicationException($"Failed to get user login status. Exit code {exitCode}: {error}");
}
public async Task<UserLoginStatus> UserLoginAsync(LoginProvider provider, ILogger? logger = default, CancellationToken cancellationToken = default)
{
logger?.LogTrace("Logging in to dev tunnel service using {LoginProvider}.", provider);
var exitCode = provider switch
{
LoginProvider.Microsoft => await _cli.UserLoginMicrosoftAsync(logger, cancellationToken).ConfigureAwait(false),
LoginProvider.GitHub => await _cli.UserLoginGitHubAsync(logger, cancellationToken).ConfigureAwait(false),
_ => throw new ArgumentException("Unsupported provider. Supported providers are 'microsoft' and 'github'.", nameof(provider)),
};
if (exitCode == 0)
{
// Login succeeded, get the login status
return await GetUserLoginStatusAsync(logger, cancellationToken).ConfigureAwait(false);
}
View on GitHub (pinned to 25830f84bd)