{"record":{"id":"9f6649cf73157c02","repo":"quickwit-oss/quickwit","slug":"err-to-string","errorCode":null,"errorMessage":"err.to_string()","messagePattern":"err\\.to_string\\(\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"quickwit/quickwit-config/src/index_config/mod.rs","lineNumber":371,"sourceCode":"        let evaluation_schedule = prepend_at_char(&self.evaluation_schedule);\n\n        Schedule::from_str(&evaluation_schedule).with_context(|| {\n            format!(\n                \"failed to parse retention evaluation schedule `{}`\",\n                self.evaluation_schedule\n            )\n        })\n    }\n\n    pub fn duration_until_next_evaluation(&self) -> anyhow::Result<Duration> {\n        let schedule = self.evaluation_schedule()?;\n        let mut schedule_iter = schedule.upcoming(Utc);\n        let future_date = schedule_iter\n            .next()\n            .expect(\"Failed to obtain next evaluation date.\");\n        let mut duration = (future_date - Utc::now())\n            .to_std()\n            .map_err(|err| anyhow::anyhow!(err.to_string()))?;\n        let jitter_max_secs = self\n            .evaluation_schedule_jitter\n            .as_deref()\n            .copied()\n            .unwrap_or_else(|| {\n                if let Some(next_next_date) = schedule_iter.next() {\n                    let time_between_schedules = next_next_date - future_date;\n                    Duration::from_secs(time_between_schedules.num_seconds().clamp(0, 3600) as u64)\n                } else {\n                    // we don't know when the schedule is. That's odd. Let's allow no jitter\n                    warn!(\"found retention policy schedule with no next execution\");\n                    Duration::ZERO\n                }\n            })\n            .as_secs();\n        let jitter = rng().sample::<u64, _>(distr::StandardUniform) % (jitter_max_secs + 1);\n        duration += Duration::from_secs(jitter);\n        Ok(duration)","sourceCodeStart":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-config/src/index_config/mod.rs#L353-L389","documentation":"RetentionConfig's `duration_until_next_evaluation` computes the time until the schedule's next occurrence using cron `Schedule::upcoming(Utc)`. The difference between the next date and now is converted to a `std::time::Duration`; if the next date is in the past (negative duration), `to_std()` fails and its Display is wrapped into this anyhow error.","triggerScenarios":"Calling duration_until_next_evaluation on a retention policy whose cron schedule's next occurrence resolves to a timestamp before now — typically a clock race: the iterator was fetched, then time advanced past `future_date` before subtraction, or the schedule produced a date <= now.","commonSituations":"Very frequent cron schedules (e.g. every second) racing with slow evaluation; system clock adjustments (NTP corrections, container clock skew); calling the function in tests with a schedule whose next tick already elapsed.","solutions":["Retry the call — the function recomputes `upcoming(Utc)` each time, so a transient race resolves on the next call.","Widen the cron interval or add jitter (`evaluation_schedule_jitter`) so ticks are not sub-second tight against evaluation latency.","Check system clock sync (NTP) if the machine's clock is jumping; verify the container/host time matches UTC expectations.","If writing tests, use a schedule comfortably in the future or inject a controlled clock."],"exampleFix":"// before\nlet duration = (future_date - Utc::now()).to_std().map_err(|err| anyhow::anyhow!(err.to_string()))?;\n// after: clamp so a just-passed tick yields zero duration instead of an error\nlet duration = (future_date - Utc::now()).to_std().unwrap_or(std::time::Duration::ZERO);","handlingStrategy":"retry","validationCode":"// No pre-call validation possible; ensure host clock is correct:\n// timedatectl | grep -i synchronized  (expect 'synchronized: yes')","typeGuard":null,"tryCatchPattern":"match duration_until_next_evaluation(config) {\n    Ok(d) => schedule_after(d),\n    Err(e) if e.to_string().contains(\"out of range\") || e.to_string().contains(\"earlier\") => {\n        // transient clock race: retry after a short backoff\n        tokio::time::sleep(Duration::from_secs(1)).await;\n        // retry\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Keep NTP/time sync enabled on hosts and containers.","Avoid cron schedules with sub-second granularity for retention evaluation.","Set evaluation_schedule_jitter so evaluations are not perfectly aligned with schedule ticks.","In tests, use schedules comfortably in the future or inject a controllable clock."],"tags":["config","scheduling","cron","time"],"backgroundTag":"invalid-date-format","analyzedSha":"a39730c5cdcd1a4fe798403737ae293999ea21f8","analyzedAt":"2026-09-08T13:19:37.784Z","contentChangedAt":"2026-09-08T13:19:37.784Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}