astrid-runtime/astrid · error

idle timeout must be at most {MAX_RUN_IDLE_TIMEOUT_SECS} sec

Error message

idle timeout must be at most {MAX_RUN_IDLE_TIMEOUT_SECS} seconds

What it means

idle_timeout enforces a one-day operational ceiling (MAX_RUN_IDLE_TIMEOUT_SECS). Values above that ceiling are rejected so runaway headless runs cannot idle indefinitely with a nonsensically large timeout.

Source

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

    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,
        message: astrid_types::ipc::IpcMessage,
    ) -> impl Future<Output = Result<()>> + Send;
}

View on GitHub (pinned to affd8760f4)

Solutions

  1. Lower --idle-timeout to at most 86400 seconds (one day).
  2. Use 86400 if you want the maximum supported idle window.
  3. Fix unit conversion in scripts (ms vs s) that inflate the value.

Example fix

// before
astrid headless --idle-timeout 999999

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

Strategy: validation

Validate before calling

fn idle_timeout_ok(secs: u64) -> bool {
    secs > 0 && secs <= 86400
}
// unit conversion sanity check:
let secs = minutes.checked_mul(60).expect("overflow converting to seconds");
assert!(idle_timeout_ok(secs), "idle timeout exceeds 86400s ceiling");

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}; cap idle timeout at 86400 seconds"),
}

Prevention

When it happens

Trigger: Invoking the headless command with an --idle-timeout value greater than MAX_RUN_IDLE_TIMEOUT_SECS (86400 seconds / one day), or calling idle_timeout with such a value programmatically.

Common situations: Users passing very large numbers (e.g. 999999) assuming more is safer; scripts converting minutes/hours incorrectly (e.g. seconds vs milliseconds); attempting to use the timeout as 'effectively forever'.

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/87a11b309e29d16e. Report an issue: GitHub.