microsoft/aspire · warning · ArgumentOutOfRangeException

Tunnel expiration must be from 1 hour through 30 days.

Error message

Tunnel expiration must be from 1 hour through 30 days.

What it means

DevTunnelOptions.ExpirationHours enforces the devtunnel service's allowed tunnel lifetime: between 1 and 720 hours (30 days). Setting a value outside that range throws ArgumentOutOfRangeException at property-set time.

Solutions

  1. Set ExpirationHours within 1..720 inclusive (e.g. 720 for 30 days)
  2. Convert days to hours if your config is expressed in days
  3. Store the value in configuration and clamp it before constructing DevTunnelOptions

Example fix

// before
options.ExpirationHours = 90; // meant 90 days -> throws
// after
options.ExpirationHours = 90 * 24; // 2160h? no: clamp
options.ExpirationHours = 720; // max allowed (30 days)
Defensive patterns

Strategy: validation

Validate before calling

int clamped = Math.Clamp(configuredHours, 1, 720);
options.ExpirationHours = clamped;

Type guard

static bool IsValidExpirationHours(int h) => h is >= 1 and <= 720;

Prevention

When it happens

Trigger: Assigning options.ExpirationHours = 0, a negative number, or more than 720 (e.g. 24*365 for a year-long tunnel) when configuring AddDevTunnel.

Common situations: Assuming expiration is in days instead of hours (30 vs 720); using 0 as a 'default/unlimited' sentinel; misreading the unit and writing 31 to mean a month of days.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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

Appendix: source

Thrown at src/Aspire.Hosting.DevTunnels/DevTunnelOptions.cs:55

    /// <summary>
    /// Gets or sets how many hours the tunnel can remain unused or unmodified before it expires.
    /// </summary>
    /// <remarks>
    /// Specify a whole number of hours from one hour through 30 days, inclusive.
    /// The value applies when creating a tunnel and when updating an existing tunnel.
    /// When <see langword="null"/>, no expiration override is sent: new tunnels use the service default
    /// and existing tunnels retain their configured expiration period.
    /// This is an idle expiration period, not a maximum hosting duration or an access-token lifetime.
    /// </remarks>
    /// <exception cref="ArgumentOutOfRangeException">The value is outside the supported range.</exception>
    public int? ExpirationHours
    {
        get => _expirationHours;
        set
        {
            if (value is < 1 or > 30 * 24)
            {
                throw new ArgumentOutOfRangeException(nameof(value), value, "Tunnel expiration must be from 1 hour through 30 days.");
            }

            _expirationHours = value;
        }
    }

    internal string RegionCode =>
        Region switch
        {
            DevTunnelRegion.WestEurope => "euw",
            DevTunnelRegion.UKSouth => "uks1",
            DevTunnelRegion.NorthEurope => "eun1",
            DevTunnelRegion.EastUs => "use",
            DevTunnelRegion.EastUs2 => "use2",
            DevTunnelRegion.WestUs2 => "usw2",
            DevTunnelRegion.WestUs3 => "usw3",
            DevTunnelRegion.CentralIndia => "inc1",
            DevTunnelRegion.SoutheastAsia => "asse",

View on GitHub (pinned to 25830f84bd)