astrid-runtime/astrid · error

idle timeout must be greater than 0 seconds

Error message

idle timeout must be greater than 0 seconds

What it means

The headless runner's idle_timeout converts a whole-second CLI value into a Duration. Zero is rejected because a 0-second idle timeout would invalidate runs immediately — the run must be given a positive idle window.

Source

Thrown at crates/astrid-cli/src/commands/headless.rs:44

pub(crate) enum HeadlessError {
    /// No active-run message arrived within the configured idle budget.
    #[error("timed out waiting for response after {timeout_secs}s idle")]
    IdleTimeout {
        /// The configured idle budget in whole seconds.
        timeout_secs: u64,
    },
    /// The daemon connection failed while collecting the response.
    #[error(transparent)]
    Read(#[from] anyhow::Error),
}

/// Validate and convert a whole-second run idle timeout.
///
/// # Errors
/// Returns an error for zero or values above the one-day operational ceiling.
pub(crate) fn idle_timeout(timeout_secs: u64) -> Result<Duration> {
    if timeout_secs == 0 {
        anyhow::bail!("idle timeout must be greater than 0 seconds");
    }
    if timeout_secs > MAX_RUN_IDLE_TIMEOUT_SECS {
        anyhow::bail!("idle timeout must be at most {MAX_RUN_IDLE_TIMEOUT_SECS} seconds");
    }
    Ok(Duration::from_secs(timeout_secs))
}

/// Source of daemon messages consumed by headless response collection.
pub(crate) trait ResponseSource {
    /// Read the next daemon message or report timeout at the source boundary.
    fn read_message_before(
        &mut self,
        remaining: Duration,
    ) -> impl Future<Output = ReadOutcome> + Send;

    /// Send a message to the daemon.
    fn send_message(
        &mut self,

View on GitHub (pinned to affd8760f4)

Solutions

  1. Pass a positive integer of seconds, e.g. --idle-timeout 60.
  2. If 'no timeout' is intended, use the maximum allowed value (up to the one-day ceiling) rather than 0.
  3. Fix the script/flag source so the computed timeout is never 0.

Example fix

// before
astrid headless --idle-timeout 0

// after
astrid headless --idle-timeout 300
Defensive patterns

Strategy: validation

Validate before calling

fn idle_timeout_ok(secs: u64) -> bool {
    secs > 0 && secs <= 86400
}
// guard before invoking the CLI:
assert!(idle_timeout_ok(secs), "idle timeout must be 1..=86400 seconds");

Type guard

fn parse_idle_timeout(raw: &str) -> Option<u64> {
    raw.parse::<u64>().ok().filter(|s| *s > 0 && *s <= 86400)
}

Try / catch

match idle_timeout(secs) {
    Ok(d) => run_with_timeout(d).await,
    Err(e) => eprintln!("astrid: {e}; pass a whole number of seconds in 1..=86400"),
}

Prevention

When it happens

Trigger: Invoking the headless command (or calling idle_timeout directly) with --idle-timeout 0 or an equivalent zero-second value.

Common situations: Scripts parameterizing the timeout where a computed value evaluates to 0; users passing 0 assuming it means 'no timeout' (it does not); copy-pasted flags with a placeholder left at 0.

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 astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/b8b77a50793af91e. Report an issue: GitHub.