{"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":null,"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(crate) fn process_now() -> Timestamp {\n    static PROCESS_NOW: OnceLock<Timestamp> = OnceLock::new();\n    *PROCESS_NOW.get_or_init(Timestamp::now)\n}\n\npub(crate) 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(crate) 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/533346cc374382b41ec5ff70536252b2e96e725c/src/duration.rs#L25-L61","documentation":"`parse_duration` converts a human duration string (via the `jiff` `Span` parser) or raw seconds into a non-negative `std::time::Duration`. Because negative durations make no sense for minimum-release-age style options, a parsed span that yields a negative duration is rejected with the offending input echoed.","triggerScenarios":"Calling `parse_duration` (via `resolve_cli_minimum_release_age` or `resolve_before_date_with_excludes`) with a relative span like `\"-90d\"` or `\"-1y\"` that resolves to a negative duration. Note the fallback path parses plain integers as seconds and is not covered by this check.","commonSituations":"Passing a negative duration to a CLI flag like `--min-age`; sign typo (`-30d` instead of `30d`); scripting that interpolates a negative variable.","solutions":["Remove the leading minus from the duration (e.g. `-90d` → `90d`)","Provide a plain integer of seconds if you intend an absolute duration","Check the flag/config source for an unintended sign"],"exampleFix":"// before\nmise outdated --min-age -90d\n// after\nmise outdated --min-age 90d","handlingStrategy":"validation","validationCode":"function isValidMinAge(s) {\n  return /^(?:\\d+(?:\\.\\d+)?(?:s|m|h|d|w|mo|y)|\\d+)$/.test(s.trim()); // positive span or integer seconds\n}\n// e.g. \"90d\", \"3600\" ok; \"-90d\" rejected before calling mise","typeGuard":null,"tryCatchPattern":"const { stdout } = child.spawnSync('mise', ['outdated', '--min-age', flag], { encoding: 'utf8' });\nif (/duration must not be negative/.test(stdout)) throw new Error(`negative min-age: ${flag}`);","preventionTips":["Never prefix duration flags with a minus","Sanitize interpolated duration variables in scripts","Prefer absolute integers (seconds) in automation"],"tags":["duration","cli","validation"],"backgroundTag":"invalid-duration-format","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}