{"record":{"id":"3c75a75e30c862e2","repo":"microsoft/aspire","slug":"failed-to-get-access-details-for-tunnelid-portnumber-exit","errorCode":null,"errorMessage":"Failed to get access details for '{tunnelId}'{portNumber}. Exit code {exitCode}: {error}","messagePattern":"Failed to get access details for '(.+?)'(.+?)\\. Exit code (.+?): (.+?)","errorType":"exception","errorClass":"DistributedApplicationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.DevTunnels/DevTunnelCliClient.cs","lineNumber":259,"sourceCode":"        throw new DistributedApplicationException($\"Failed to create port '{portNumber}' for tunnel '{tunnelId}' after {attempts} attempts. Exit code {exitCode}: {error}\");\n    }\n\n    public async Task<DevTunnelPortDeleteResult> DeletePortAsync(string tunnelId, int portNumber, ILogger? logger = default, CancellationToken cancellationToken = default)\n    {\n        logger?.LogTrace(\"Deleting port '{PortNumber}' on dev tunnel '{TunnelId}'.\", portNumber, tunnelId);\n        var (result, exitCode, error) = await CallCliAsJsonAsync<DevTunnelPortDeleteResult>(\n            (stdout, stderr, log, ct) => _cli.DeletePortAsync(tunnelId, portNumber, stdout, stderr, log, ct),\n            logger, cancellationToken).ConfigureAwait(false);\n        return result ?? throw new DistributedApplicationException($\"Failed to delete port '{portNumber}' on dev tunnel '{tunnelId}'. Exit code {exitCode}: {error}\");\n    }\n\n    public async Task<DevTunnelAccessStatus> GetAccessAsync(string tunnelId, int? portNumber = null, ILogger? logger = default, CancellationToken cancellationToken = default)\n    {\n        logger?.LogTrace(\"Getting access details for {PortInfo}dev tunnel '{TunnelId}'.\", portNumber.HasValue ? $\"port '{portNumber}' on \" : string.Empty, tunnelId);\n        var (access, exitCode, error) = await CallCliAsJsonAsync<DevTunnelAccessStatus>(\n            (stdout, stderr, log, ct) => _cli.ListAccessAsync(tunnelId, portNumber, stdout, stderr, log, ct),\n            logger, cancellationToken).ConfigureAwait(false);\n        return access ?? throw new DistributedApplicationException($\"Failed to get access details for '{tunnelId}'{(portNumber.HasValue ? $\" port {portNumber}\" : \"\")}. Exit code {exitCode}: {error}\");\n    }\n\n    public async Task<UserLoginStatus> GetUserLoginStatusAsync(ILogger? logger = default, CancellationToken cancellationToken = default)\n    {\n        logger?.LogTrace(\"Getting dev tunnel user login status.\");\n        var (login, exitCode, error) = await CallCliAsJsonAsync<UserLoginStatus>(\n            _cli.UserStatusAsync,\n            logger, cancellationToken).ConfigureAwait(false);\n        return login ?? throw new DistributedApplicationException($\"Failed to get user login status. Exit code {exitCode}: {error}\");\n    }\n\n    public async Task<UserLoginStatus> UserLoginAsync(LoginProvider provider, ILogger? logger = default, CancellationToken cancellationToken = default)\n    {\n        logger?.LogTrace(\"Logging in to dev tunnel service using {LoginProvider}.\", provider);\n        var exitCode = provider switch\n        {\n            LoginProvider.Microsoft => await _cli.UserLoginMicrosoftAsync(logger, cancellationToken).ConfigureAwait(false),\n            LoginProvider.GitHub => await _cli.UserLoginGitHubAsync(logger, cancellationToken).ConfigureAwait(false),","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.DevTunnels/DevTunnelCliClient.cs#L241-L277","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Confirm the tunnelId exists (`devtunnel list`) and has not expired (expiration is 1 hour to 30 days)","Re-authenticate with `devtunnel user login` if the CLI error indicates not logged in","Check the error text in the message for the underlying CLI reason","Only query access for ports you previously created/configured"],"exampleFix":"// before\nvar status = await client.GetAccessAsync(\"staging-tunnel\", 443); // throws if tunnel missing\n// after\nvar tunnels = await client.ListTunnelsAsync();\nif (tunnels.Any(t => t.TunnelId == \"staging-tunnel\"))\n{\n    var status = await client.GetAccessAsync(\"staging-tunnel\", 443);\n}","handlingStrategy":"try-catch","validationCode":"var tunnels = await client.ListTunnelsAsync(logger, ct);\nif (!tunnels.Any(t => t.TunnelId == tunnelId)) throw new InvalidOperationException($\"Tunnel '{tunnelId}' not found\");","typeGuard":"static bool IsValidTunnelId(string? id) => !string.IsNullOrEmpty(id) && id.All(c => char.IsAsciiLetterOrDigit(c) || c == '-');","tryCatchPattern":"try { var access = await client.GetAccessAsync(tunnelId, port, logger, ct); }\ncatch (DistributedApplicationException ex) { logger.LogError(ex, \"Access lookup failed for {Tunnel}\", tunnelId); }","preventionTips":["Confirm tunnel existence and non-expiration before querying access","Re-login with devtunnel when auth-related exit codes appear","Only query ports you configured"],"tags":["devtunnels","cli","access"],"backgroundTag":"cli-command-failed","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}