{"record":{"id":"87a11b309e29d16e","repo":"astrid-runtime/astrid","slug":"idle-timeout-must-be-at-most-max-run-idle-timeout","errorCode":null,"errorMessage":"idle timeout must be at most {MAX_RUN_IDLE_TIMEOUT_SECS} seconds","messagePattern":"idle timeout must be at most (.+?) seconds","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/headless.rs","lineNumber":47,"sourceCode":"    IdleTimeout {\n        /// The configured idle budget in whole seconds.\n        timeout_secs: u64,\n    },\n    /// The daemon connection failed while collecting the response.\n    #[error(transparent)]\n    Read(#[from] anyhow::Error),\n}\n\n/// Validate and convert a whole-second run idle timeout.\n///\n/// # Errors\n/// Returns an error for zero or values above the one-day operational ceiling.\npub(crate) fn idle_timeout(timeout_secs: u64) -> Result<Duration> {\n    if timeout_secs == 0 {\n        anyhow::bail!(\"idle timeout must be greater than 0 seconds\");\n    }\n    if timeout_secs > MAX_RUN_IDLE_TIMEOUT_SECS {\n        anyhow::bail!(\"idle timeout must be at most {MAX_RUN_IDLE_TIMEOUT_SECS} seconds\");\n    }\n    Ok(Duration::from_secs(timeout_secs))\n}\n\n/// Source of daemon messages consumed by headless response collection.\npub(crate) trait ResponseSource {\n    /// Read the next daemon message or report timeout at the source boundary.\n    fn read_message_before(\n        &mut self,\n        remaining: Duration,\n    ) -> impl Future<Output = ReadOutcome> + Send;\n\n    /// Send a message to the daemon.\n    fn send_message(\n        &mut self,\n        message: astrid_types::ipc::IpcMessage,\n    ) -> impl Future<Output = Result<()>> + Send;\n}","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/headless.rs#L29-L65","documentation":"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.","triggerScenarios":"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.","commonSituations":"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'.","solutions":["Lower --idle-timeout to at most 86400 seconds (one day).","Use 86400 if you want the maximum supported idle window.","Fix unit conversion in scripts (ms vs s) that inflate the value."],"exampleFix":"// before\nastrid headless --idle-timeout 999999\n\n// after\nastrid headless --idle-timeout 86400","handlingStrategy":"validation","validationCode":"fn idle_timeout_ok(secs: u64) -> bool {\n    secs > 0 && secs <= 86400\n}\n// unit conversion sanity check:\nlet secs = minutes.checked_mul(60).expect(\"overflow converting to seconds\");\nassert!(idle_timeout_ok(secs), \"idle timeout exceeds 86400s ceiling\");","typeGuard":"fn parse_idle_timeout(raw: &str) -> Option<u64> {\n    raw.parse::<u64>().ok().filter(|s| *s > 0 && *s <= 86400)\n}","tryCatchPattern":"match idle_timeout(secs) {\n    Ok(d) => run_with_timeout(d).await,\n    Err(e) => eprintln!(\"astrid: {e}; cap idle timeout at 86400 seconds\"),\n}","preventionTips":["Convert units carefully — seconds, not milliseconds — when generating flags.","Use 86400 (one day) as the maximum rather than arbitrary large values.","Add a pre-invocation clamp: min(max(secs, 1), 86400)."],"tags":["cli","timeout","validation"],"backgroundTag":"value-out-of-range","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}