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

  1. Run `devtunnel user login` interactively to establish credentials, then retry
  2. Verify the devtunnel CLI is installed and on PATH (`devtunnel --version`)
  3. Inspect the exit code and error text appended to the message for the root cause
  4. 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

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


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)