{"record":{"id":"13c16991eb291d9c","repo":"jdx/mise","slug":"duration-must-not-be-negative","errorCode":null,"errorMessage":"duration must not be negative: {}","messagePattern":"duration must not be negative: (.+?)","errorType":"validation","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/duration.rs","lineNumber":43,"sourceCode":"///\n/// This is used for resolving relative durations (e.g. `minimum_release_age = \"3d\"`)\n/// consistently: every resolution of the same relative duration within a single\n/// mise invocation produces the same absolute timestamp, and downstream code\n/// that converts the absolute timestamp back to a duration (e.g. for npm's\n/// `--min-release-age`) gets the exact duration the user specified rather than\n/// a slightly-larger value due to wall clock drift between phases.\npub fn process_now() -> Timestamp {\n    static PROCESS_NOW: OnceLock<Timestamp> = OnceLock::new();\n    *PROCESS_NOW.get_or_init(Timestamp::now)\n}\n\npub fn parse_duration(s: &str) -> Result<Duration> {\n    match s.parse::<Span>() {\n        Ok(span) => {\n            // we must provide a relative date to determine the duration with months and years\n            let duration = span.to_duration(date(2025, 1, 1))?;\n            if duration.is_negative() {\n                bail!(\"duration must not be negative: {}\", s);\n            }\n            Ok(duration.unsigned_abs())\n        }\n        Err(_) => Ok(Duration::from_secs(s.parse()?)),\n    }\n}\n\n/// Parse a date/duration string into a Timestamp.\n/// Supports:\n/// - RFC3339 timestamps: \"2024-06-01T12:00:00Z\"\n/// - ISO dates: \"2024-06-01\" (treated as end of day in UTC)\n/// - Relative durations: \"90d\", \"1y\", \"6m\" (subtracted from now)\n///\n/// Relative durations are anchored to [`process_now`] so all resolutions\n/// within a single mise invocation agree on \"now\".\npub fn parse_into_timestamp(s: &str) -> Result<Timestamp> {\n    // Try RFC3339 timestamp first\n    if let Ok(ts) = s.parse::<Timestamp>() {","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/duration.rs#L25-L61","documentation":"`parse_duration` (src/duration.rs:36) first parses the string as a jiff `Span` (`90d`, `1y 6m`) and converts it relative to a fixed date. A negative span (leading `-`) yields a negative duration, which timeout/expiry semantics cannot use, so it bails; only if the string is not a span at all does it fall back to plain seconds parsing.","triggerScenarios":"Any setting parsed with `parse_duration` — e.g. deps provider `timeout` values (see the timeout parsing in src/deps/mod.rs that warns on invalid timeouts) — receiving a negative span string such as `\"-5m\"` or `\"-1h30m\"`.","commonSituations":"Pasting negative durations from deadline-style examples (\"time until expiry\") into a duration field; templated values that render `-<n>`; mixing up 'duration' with 'point in time' semantics and negating the value.","solutions":["Drop the leading minus: `5m` instead of `-5m`","If you meant 'as short as possible', use the smallest accepted positive duration or the setting's zero/minimum","Check the setting's docs for whether span syntax (`90d`) or plain seconds is expected"],"exampleFix":"# before\n[deps.providers.npm]\ntimeout = \"-5m\"\n\n# after\n[deps.providers.npm]\ntimeout = \"5m\"","handlingStrategy":"validation","validationCode":"# reject negative durations before they reach mise\nimport re, sys\nval = \"5m\"\nif re.match(r\"^-\", val) or val.lstrip(\"-\").strip() == \"\":\n    sys.exit(\"duration must be positive\")\nprint(\"ok\")","typeGuard":"fn is_positive_span(s: &str) -> bool {\n    s.parse::<jiff::Span>().ok()\n        .and_then(|sp| sp.to_duration(jiff::civil::date(2025, 1, 1)).ok())\n        .is_some_and(|d| !d.is_negative())\n}","tryCatchPattern":null,"preventionTips":["Remember duration fields are lengths, not deadlines — never negate them","Use plain positive spans (`5m`, `1h`) and let the setting define the semantics","Validate templated duration values in CI before they reach mise"],"tags":["mise","duration","config","validation"],"backgroundTag":"invalid-duration-format","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}