microsoft/aspire · error · DistributedApplicationException

Failed to get access details for

Error message

Failed to get access details for '{tunnelId}'{portNumber}. Exit code {exitCode}: {error}

What it means

GetAccessAsync runs `devtunnel access list` for a tunnel (optionally a specific port) and deserializes the JSON. When the CLI exits non-zero and no access status can be produced, this DistributedApplicationException is thrown with the exit code and CLI error output.

Solutions

  1. Confirm the tunnelId exists (`devtunnel list`) and has not expired (expiration is 1 hour to 30 days)
  2. Re-authenticate with `devtunnel user login` if the CLI error indicates not logged in
  3. Check the error text in the message for the underlying CLI reason
  4. Only query access for ports you previously created/configured

Example fix

// before
var status = await client.GetAccessAsync("staging-tunnel", 443); // throws if tunnel missing
// after
var tunnels = await client.ListTunnelsAsync();
if (tunnels.Any(t => t.TunnelId == "staging-tunnel"))
{
    var status = await client.GetAccessAsync("staging-tunnel", 443);
}
Defensive patterns

Strategy: try-catch

Validate before calling

var tunnels = await client.ListTunnelsAsync(logger, ct);
if (!tunnels.Any(t => t.TunnelId == tunnelId)) throw new InvalidOperationException($"Tunnel '{tunnelId}' not found");

Type guard

static bool IsValidTunnelId(string? id) => !string.IsNullOrEmpty(id) && id.All(c => char.IsAsciiLetterOrDigit(c) || c == '-');

Try / catch

try { var access = await client.GetAccessAsync(tunnelId, port, logger, ct); }
catch (DistributedApplicationException ex) { logger.LogError(ex, "Access lookup failed for {Tunnel}", tunnelId); }

Prevention

When it happens

Trigger: Calling GetAccessAsync for a tunnelId that does not exist or expired, an unauthenticated/expired devtunnel login, a port that has no access configuration, or any CLI invocation failing to produce JSON.

Common situations: Tunnel was auto-deleted after expiration; tunnelId derived from a different AppHost name/hash; anonymous access probing before login; querying a port before access was set.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/3c75a75e30c862e2. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.DevTunnels/DevTunnelCliClient.cs:259

        throw new DistributedApplicationException($"Failed to create port '{portNumber}' for tunnel '{tunnelId}' after {attempts} attempts. Exit code {exitCode}: {error}");
    }

    public async Task<DevTunnelPortDeleteResult> DeletePortAsync(string tunnelId, int portNumber, ILogger? logger = default, CancellationToken cancellationToken = default)
    {
        logger?.LogTrace("Deleting port '{PortNumber}' on dev tunnel '{TunnelId}'.", portNumber, tunnelId);
        var (result, exitCode, error) = await CallCliAsJsonAsync<DevTunnelPortDeleteResult>(
            (stdout, stderr, log, ct) => _cli.DeletePortAsync(tunnelId, portNumber, stdout, stderr, log, ct),
            logger, cancellationToken).ConfigureAwait(false);
        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),

View on GitHub (pinned to 25830f84bd)