jdx/mise · error · eyre::Report
duration must not be negative: {}
Error message
duration must not be negative: {} What it means
`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.
Source
Thrown at src/duration.rs:43
///
/// This is used for resolving relative durations (e.g. `minimum_release_age = "3d"`)
/// consistently: every resolution of the same relative duration within a single
/// mise invocation produces the same absolute timestamp, and downstream code
/// that converts the absolute timestamp back to a duration (e.g. for npm's
/// `--min-release-age`) gets the exact duration the user specified rather than
/// a slightly-larger value due to wall clock drift between phases.
pub fn process_now() -> Timestamp {
static PROCESS_NOW: OnceLock<Timestamp> = OnceLock::new();
*PROCESS_NOW.get_or_init(Timestamp::now)
}
pub fn parse_duration(s: &str) -> Result<Duration> {
match s.parse::<Span>() {
Ok(span) => {
// we must provide a relative date to determine the duration with months and years
let duration = span.to_duration(date(2025, 1, 1))?;
if duration.is_negative() {
bail!("duration must not be negative: {}", s);
}
Ok(duration.unsigned_abs())
}
Err(_) => Ok(Duration::from_secs(s.parse()?)),
}
}
/// Parse a date/duration string into a Timestamp.
/// Supports:
/// - RFC3339 timestamps: "2024-06-01T12:00:00Z"
/// - ISO dates: "2024-06-01" (treated as end of day in UTC)
/// - Relative durations: "90d", "1y", "6m" (subtracted from now)
///
/// Relative durations are anchored to [`process_now`] so all resolutions
/// within a single mise invocation agree on "now".
pub fn parse_into_timestamp(s: &str) -> Result<Timestamp> {
// Try RFC3339 timestamp first
if let Ok(ts) = s.parse::<Timestamp>() {View on GitHub (pinned to 9dcfcaa0dc)
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
Example fix
# before [deps.providers.npm] timeout = "-5m" # after [deps.providers.npm] timeout = "5m"
Defensive patterns
Strategy: validation
Validate before calling
# reject negative durations before they reach mise
import re, sys
val = "5m"
if re.match(r"^-", val) or val.lstrip("-").strip() == "":
sys.exit("duration must be positive")
print("ok") Type guard
fn is_positive_span(s: &str) -> bool {
s.parse::<jiff::Span>().ok()
.and_then(|sp| sp.to_duration(jiff::civil::date(2025, 1, 1)).ok())
.is_some_and(|d| !d.is_negative())
} Prevention
- 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
When it happens
Trigger: 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"`.
Common situations: 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.
Related errors
- Invalid date or duration: {s}. Expected formats: '2024-06-01
- deps run command cannot be empty
- bootstrap compose project names cannot be empty
- bootstrap compose project '{name}' wait_timeout requires wai
- bootstrap compose project '{name}' wait_timeout must be grea
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/13c16991eb291d9c.
Report an issue: GitHub.