microsoft/aspire · error · ArgumentException
The label ' ' is invalid. A valid label must: - consist of…
Error message
The label '{label}' is invalid. A valid label must:
- consist of letters, numbers, underscores, hyphens, or equals signs
- be 1-50 characters long
What it means
AddDevTunnel validates every label in options.Labels (labels must be 1-50 chars of letters, digits, underscores, hyphens, or equals signs) via TryValidateLabels; on failure it throws ArgumentException with the multi-line message, attributing it to the options parameter. Note Aspire itself adds an `aspire_{name}-{appHostId}` label, so an invalid resource name can also trigger this.
Solutions
- Sanitize each label: letters, numbers, underscore, hyphen, equals only, max 50 chars
- Remove or replace offending labels before calling AddDevTunnel
- If the auto-added aspire_ label is the problem, shorten/normalize the resource `name` passed to AddDevTunnel
Example fix
// before
options.Labels = ["team:platform", "env.production Very Long Label Over Fifty CharactersXXXXXXXXXXXXXXXX"];
builder.AddDevTunnel("api", options); // throws
// after
options.Labels = ["team=platform", "env=prod"];
builder.AddDevTunnel("api", options); Defensive patterns
Strategy: validation
Validate before calling
static bool IsValidLabel(string label) =>
label.Length is >= 1 and <= 50 && label.All(c => char.IsLetterOrDigit(c) || c is '_' or '-' or '=');
if (options.Labels?.Any(l => !IsValidLabel(l)) == true)
throw new InvalidOperationException("One or more dev tunnel labels are invalid"); Type guard
static bool IsValidLabel(string? label) =>
!string.IsNullOrEmpty(label) && label.Length <= 50 && label.All(c => char.IsLetterOrDigit(c) || c is '_' or '-' or '='); Try / catch
try { builder.AddDevTunnel(name, options); }
catch (ArgumentException ex) when (ex.Message.Contains("label"))
{ logger.LogError(ex, "Invalid dev tunnel labels"); throw; } Prevention
- Replace ':' or '.' separators in labels with '=' or '-'
- Keep labels at most 50 characters
- Remember the auto-added aspire_{name}-{hash} label must also be valid, so keep the resource name simple
When it happens
Trigger: Calling AddDevTunnel with options.Labels containing spaces, periods, characters outside [A-Za-z0-9_- =], empty strings, or labels longer than 50 characters — or with a resource `name` that produces an invalid auto-added aspire_* label.
Common situations: Using dots in labels (common convention, e.g. 'env.prod'); long labels exceeding 50 chars; labels copied from other tagging systems with slashes or colons.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Cannot tunnel endpoint
- Invalid protocol ' ' specified in port options. Supported…
- {result.ValidationMessage}
- The tunnel ID ' ' is invalid. A valid tunnel ID must: -…
- -32602
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/a6281d047b256712.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.DevTunnels/DevTunnelResourceBuilderExtensions.cs:79
// 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
builder.Services.TryAddSingleton<DevTunnelLoginManager>();
builder.Services.TryAddSingleton<LoggedOutNotificationManager>();
builder.Services.TryAddSingleton<IDevTunnelClient, DevTunnelCliClient>();
var workingDirectory = builder.AppHostDirectory;
var tunnelResource = new DevTunnelResource(name, tunnelId, DevTunnelCli.GetCliPath(builder.Configuration), workingDirectory, options);
// Health check
var healtCheckKey = $"{name}-check";
builder.Services.AddHealthChecks().Add(new HealthCheckRegistration(
healtCheckKey,
services => new DevTunnelHealthCheck(
services.GetRequiredService<IDevTunnelClient>(),
services.GetRequiredService<LoggedOutNotificationManager>(),
tunnelResource,View on GitHub (pinned to 25830f84bd)