windmill-labs/windmill · warning
cron: a panic occurred during find_next_occurrence
Error message
cron: a panic occurred during find_next_occurrence
What it means
Utils::from_str validates a cron schedule by computing its next occurrence with the croner crate inside catch_unwind. If find_next_occurrence panics (croner has edge cases that panic on certain expressions, e.g. extreme step values or overflow in day-of-month/DOW computation), the panic is caught and converted into Error::BadRequest("cron: a panic occurred during find_next_occurrence").
Source
Thrown at backend/windmill-common/src/utils.rs:1058
e
);
Error::BadRequest(format!(
"cron: {}{}",
e,
six_fields_hint(schedule_str, version, seconds_required)
))
})
});
// Additional check to make sure the provided schedule can generate a next event
if let Ok(ScheduleType::Croner(croner_schedule)) = &schedule_type_result {
let test_time = chrono::Utc::now().with_timezone(&chrono_tz::UTC);
let result = panic::catch_unwind(AssertUnwindSafe(|| {
croner_schedule
.find_next_occurrence(&test_time, false)
.expect("cron: a schedule should have a next event");
}));
if let Err(_) = result {
tracing::error!("A panic occurred while finding the next occurrence");
return Err(Error::BadRequest(format!(
"cron: a panic occurred during find_next_occurrence"
)));
}
if let Err(e) = result {
tracing::error!(
"An error occurred while finding the next occurrence: {:?}",
e
);
return Err(Error::BadRequest(format!(
"cron: error during find_next_occurrence: {:?}",
e
)));
}
}
View on GitHub (pinned to e474e8803c)
Solutions
- Inspect the schedule string the user submitted and simplify/normalize it to a standard 5- or 6-field cron expression.
- Test the expression with croner locally (or a cron linter like crontab.guru) to find the unsupported construct.
- Update the croner dependency — newer versions fix panics on edge-case expressions (check backend Cargo.lock).
- As the user, re-enter a valid schedule that computes a next occurrence, e.g. replace extreme step values with a concrete field list.
Example fix
// before let schedule = "0 0 30 2 *"; // Feb 30 - no such date, croner may panic scanning forward Schedule::from_str(schedule)?; // after let schedule = "0 0 1 3 *"; // use an existing date, or a supported expression Schedule::from_str(schedule)?;
Defensive patterns
Strategy: validation
Validate before calling
// validate the schedule string before submitting it
fn valid_schedule(s: &str) -> bool {
use cron::Schedule;
s.parse::<Schedule>()
.map(|sch| sch.upcoming(chrono_tz::UTC).next().is_some())
.unwrap_or(false)
} Try / catch
// caller side: from_str returns Result, match it
match Schedule::from_str(&input) {
Ok(s) => save_schedule(s),
Err(Error::BadRequest(msg)) => show_validation_error(msg),
Err(e) => return Err(e),
} Prevention
- Lint cron expressions (crontab.guru or equivalent) before saving schedules.
- Keep the croner/cron dependency updated to pick up panic fixes on edge-case expressions.
- Avoid exotic step values and impossible date combinations (e.g. Feb 30) in user-facing schedule inputs.
- Round-trip test any schedule parsed from another cron dialect before deploying it.
When it happens
Trigger: Parsing a schedule string with Schedule::from_str where croner's find_next_occurrence panics on the expression — typically unusual cron syntaxes like very large step values (*/99999999999), combos that overflow when scanning for the next matching date (e.g. Feb 30-like constraints), or expressions croner doesn't fully support.
Common situations: Users entering hand-written cron schedules in the Windmill UI when configuring flow schedule triggers; schedules migrated from other cron dialects with unsupported syntax; year/timezone edge cases when scanning far into the future.
Related errors
- Invalid schedule or timezone: ${formatToolError(error)}
- unreachable, language is not supported: {language:#?}
- itered cannot be empty
- Field properties should be an object
- got value: {other} for field `type`, expected value: `static
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/2bf6c1a6c42ef5e2.
Report an issue: GitHub.