{"record":{"id":"bac47057a9645c0f","repo":"microsoft/aspire","slug":"tunnel-expiration-must-be-from-1-hour-through-30-days","errorCode":null,"errorMessage":"Tunnel expiration must be from 1 hour through 30 days.","messagePattern":"Tunnel expiration must be from 1 hour through 30 days\\.","errorType":"validation","errorClass":"ArgumentOutOfRangeException","httpStatus":null,"severity":"warning","filePath":"src/Aspire.Hosting.DevTunnels/DevTunnelOptions.cs","lineNumber":55,"sourceCode":"    /// <summary>\n    /// Gets or sets how many hours the tunnel can remain unused or unmodified before it expires.\n    /// </summary>\n    /// <remarks>\n    /// Specify a whole number of hours from one hour through 30 days, inclusive.\n    /// The value applies when creating a tunnel and when updating an existing tunnel.\n    /// When <see langword=\"null\"/>, no expiration override is sent: new tunnels use the service default\n    /// and existing tunnels retain their configured expiration period.\n    /// This is an idle expiration period, not a maximum hosting duration or an access-token lifetime.\n    /// </remarks>\n    /// <exception cref=\"ArgumentOutOfRangeException\">The value is outside the supported range.</exception>\n    public int? ExpirationHours\n    {\n        get => _expirationHours;\n        set\n        {\n            if (value is < 1 or > 30 * 24)\n            {\n                throw new ArgumentOutOfRangeException(nameof(value), value, \"Tunnel expiration must be from 1 hour through 30 days.\");\n            }\n\n            _expirationHours = value;\n        }\n    }\n\n    internal string RegionCode =>\n        Region switch\n        {\n            DevTunnelRegion.WestEurope => \"euw\",\n            DevTunnelRegion.UKSouth => \"uks1\",\n            DevTunnelRegion.NorthEurope => \"eun1\",\n            DevTunnelRegion.EastUs => \"use\",\n            DevTunnelRegion.EastUs2 => \"use2\",\n            DevTunnelRegion.WestUs2 => \"usw2\",\n            DevTunnelRegion.WestUs3 => \"usw3\",\n            DevTunnelRegion.CentralIndia => \"inc1\",\n            DevTunnelRegion.SoutheastAsia => \"asse\",","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.DevTunnels/DevTunnelOptions.cs#L37-L73","documentation":"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.","triggerScenarios":"Assigning options.ExpirationHours = 0, a negative number, or more than 720 (e.g. 24*365 for a year-long tunnel) when configuring AddDevTunnel.","commonSituations":"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.","solutions":["Set ExpirationHours within 1..720 inclusive (e.g. 720 for 30 days)","Convert days to hours if your config is expressed in days","Store the value in configuration and clamp it before constructing DevTunnelOptions"],"exampleFix":"// before\noptions.ExpirationHours = 90; // meant 90 days -> throws\n// after\noptions.ExpirationHours = 90 * 24; // 2160h? no: clamp\noptions.ExpirationHours = 720; // max allowed (30 days)","handlingStrategy":"validation","validationCode":"int clamped = Math.Clamp(configuredHours, 1, 720);\noptions.ExpirationHours = clamped;","typeGuard":"static bool IsValidExpirationHours(int h) => h is >= 1 and <= 720;","tryCatchPattern":null,"preventionTips":["Remember the unit is hours, max 720 (30 days)","Clamp config-derived values before assignment","Never use 0 as a sentinel for expiration"],"tags":["devtunnels","configuration","argument-out-of-range"],"backgroundTag":"value-out-of-range","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T09:17:21.228Z"}