{"record":{"id":"6ba104a41cbed346","repo":"tinyhumansai/openhuman","slug":"no-future-occurrence-found-within-active-hours-aft","errorCode":null,"errorMessage":"No future occurrence found within active hours after 100,000 attempts","messagePattern":"No future occurrence found within active hours after 100,000 attempts","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/cron/schedule.rs","lineNumber":45,"sourceCode":"                    if active.contains(local_t) {\n                        return Ok(next_utc);\n                    }\n                    tracing::debug!(\n                        \"[cron] next_run candidate {} outside active window {}–{}, advancing\",\n                        next_utc,\n                        active.start,\n                        active.end\n                    );\n                    current_from = next_utc;\n                } else {\n                    return Ok(next_utc);\n                }\n            }\n            tracing::warn!(\n                \"[cron] no occurrence found within active_hours for expr={} after 100,000 candidates\",\n                expr\n            );\n            anyhow::bail!(\"No future occurrence found within active hours after 100,000 attempts\")\n        }\n        Schedule::At { at } => Ok(*at),\n        Schedule::Every { every_ms } => {\n            if *every_ms == 0 {\n                anyhow::bail!(\"Invalid schedule: every_ms must be > 0\");\n            }\n            let ms = i64::try_from(*every_ms).context(\"every_ms is too large\")?;\n            let delta = ChronoDuration::milliseconds(ms);\n            from.checked_add_signed(delta)\n                .ok_or_else(|| anyhow::anyhow!(\"every_ms overflowed DateTime\"))\n        }\n    }\n}\n\npub fn validate_schedule(schedule: &Schedule, now: DateTime<Utc>) -> Result<()> {\n    match schedule {\n        Schedule::Cron {\n            expr,","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/cron/schedule.rs#L27-L63","documentation":"For Schedule::Cron with active_hours, next_run_for_schedule walks candidate occurrences and returns the first inside the active window. If 100,000 consecutive candidates all fall outside the window it gives up: as configured, the job can never fire. validate_schedule calls this function, so the error typically surfaces at job create/update time.","triggerScenarios":"An expression that only fires outside the window — e.g. expr '0 3 * * *' (03:00) with active_hours '09:00-17:00'; timezone mismatch between the expression's tz and the window's clock; a narrowed window that no longer contains any fire time.","commonSituations":"Setting active_hours in local time while the expression is evaluated in the job tz; editing the window after the expression was chosen; DST shifts pushing the fire time out of the window; copy-pasted active_hours from a different job.","solutions":["Widen active_hours so at least one expression fire time is inside it — or remove active_hours entirely","Shift the expression into the window (e.g. '0 3 * * *' → '0 10 * * *' for a 09:00-17:00 window)","Set the job's tz so expression and active_hours agree on the clock, then re-validate"],"exampleFix":"// before\nSchedule::Cron { expr: \"0 3 * * *\".into(), tz: None, active_hours: Some(\"09:00-17:00\".into()) }\n\n// after\nSchedule::Cron { expr: \"0 10 * * *\".into(), tz: None, active_hours: Some(\"09:00-17:00\".into()) }","handlingStrategy":"validation","validationCode":"// Authoring-time check: the job must have a reachable next run before persisting\nvalidate_schedule(&schedule, Utc::now())?;\n// or directly:\nnext_run_for_schedule(&schedule, Utc::now())?;","typeGuard":null,"tryCatchPattern":"At job-create/update handlers, catch this bail distinctly and return 'schedule never fires within active_hours' so the UI can highlight the active-hours field rather than the expression (or vice versa).","preventionTips":["Sanity-check that at least one expression fire time lies inside active_hours before saving","State the timezone next to active-hours inputs to prevent local/UTC mix-ups","Re-validate jobs after DST changes or window edits"],"tags":["cron","schedule","active-hours","validation"],"backgroundTag":"cron-no-next-occurrence","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}