microsoft/aspire · error · ArgumentException

The tunnel ID ' ' is invalid. A valid tunnel ID must: -…

Error message

The tunnel ID '{tunnelId}' is invalid. A valid tunnel ID must:
- start and end with a letter or number
- consist of lowercase letters, numbers, and hyphens
- be 1-58 characters long

What it means

AddDevTunnel validates the generated or supplied tunnel ID against the devtunnel service rules: lowercase letters, numbers, and hyphens, starting/ending alphanumeric, 1-58 chars (regex [a-z0-9][a-z0-9-]{1,58}[a-z0-9]). Invalid IDs throw this ArgumentException before any CLI call.

Solutions

  1. Provide an explicit tunnelId of lowercase letters, numbers, and hyphens, 1-58 chars, not starting/ending with a hyphen
  2. Pre-lowercase and normalize (replace '_' with '-') before calling AddDevTunnel
  3. Shorten the name if the derived `{name}-{appHostId}` exceeds 58 characters

Example fix

// before
builder.AddDevTunnel("my_Tunnel"); // underscore + uppercase invalid
// after
builder.AddDevTunnel("my-tunnel");
Defensive patterns

Strategy: validation

Validate before calling

static string NormalizeTunnelId(string name) =>
    new string(name.ToLowerInvariant().Select(c => char.IsAsciiLetterOrDigit(c) ? c : '-').ToArray()).Trim('-') is { Length: > 0 } s
        ? (s.Length <= 58 ? s : s[..58]).TrimEnd('-')
        : "tunnel";

Type guard

static bool IsValidTunnelId(string? id) =>
    id is not null && System.Text.RegularExpressions.Regex.IsMatch(id, "^[a-z0-9][a-z0-9-]{0,56}[a-z0-9]$|^[a-z0-9]$", System.Text.RegularExpressions.RegexOptions.None, TimeSpan.FromSeconds(1));

Try / catch

try { builder.AddDevTunnel(tunnelId); }
catch (ArgumentException ex) when (ex.Message.Contains("tunnel ID"))
{ logger.LogError(ex, "Invalid tunnel id '{Id}'", tunnelId); throw; }

Prevention

When it happens

Trigger: Calling AddDevTunnel/AddDevTunnelForPolyglot with an explicit tunnelId containing uppercase letters, spaces, underscores, leading/trailing hyphens, or >58 characters.

Common situations: Passing a project name with underscores or mixed case as tunnelId; long resource names exceeding 58 chars even after the 8-char AppHost hash suffix is appended; non-ASCII names.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at src/Aspire.Hosting.DevTunnels/DevTunnelResourceBuilderExtensions.cs:64

    /// </code>
    /// </example>
    [AspireExportIgnore(Reason = "Use the dedicated polyglot overload instead.")]
    public static IResourceBuilder<DevTunnelResource> AddDevTunnel(
        this IDistributedApplicationBuilder builder,
        [ResourceName] string name,
        string? tunnelId = null,
        DevTunnelOptions? options = null)
    {
        ArgumentNullException.ThrowIfNull(builder);
        ArgumentNullException.ThrowIfNull(name);

        var appHostId = builder.Configuration["AppHost:Sha256"]?[..8];
        tunnelId ??= $"{name}-{appHostId}".ToLowerInvariant();

        // Validate the TunnelId format [a-z0-9][a-z0-9-]{1,58}[a-z0-9]
        if (!TunnelIdRegex().IsMatch(tunnelId))
        {
            throw new ArgumentException($"""
                The tunnel ID '{tunnelId}' is invalid. A valid tunnel ID must:
                - start and end with a letter or number
                - consist of lowercase letters, numbers, and hyphens
                - be 1-58 characters long
                """, nameof(tunnelId));
        }

        options ??= new DevTunnelOptions();
        options.Labels ??= [];
        options.Labels.Add($"aspire_{name}-{appHostId}");
        options.Description ??= $"Dev tunnel for '{name}' in Aspire AppHost '{builder.Environment.ApplicationName}'";

        if (!TryValidateLabels(options.Labels, out var errorMessage))
        {
            throw new ArgumentException(errorMessage, nameof(options));
        }

        // Add services

View on GitHub (pinned to 25830f84bd)