{"record":{"id":"2bf6c1a6c42ef5e2","repo":"windmill-labs/windmill","slug":"cron-a-panic-occurred-during-find-next-occurrence","errorCode":null,"errorMessage":"cron: a panic occurred during find_next_occurrence","messagePattern":"cron: a panic occurred during find_next_occurrence","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"backend/windmill-common/src/utils.rs","lineNumber":1058,"sourceCode":"                            e\n                        );\n                        Error::BadRequest(format!(\n                            \"cron: {}{}\",\n                            e,\n                            six_fields_hint(schedule_str, version, seconds_required)\n                        ))\n                    })\n                });\n\n                // Additional check to make sure the provided schedule can generate a next event\n                if let Ok(ScheduleType::Croner(croner_schedule)) = &schedule_type_result {\n                    let test_time = chrono::Utc::now().with_timezone(&chrono_tz::UTC);\n                    let result = panic::catch_unwind(AssertUnwindSafe(|| {\n                        croner_schedule\n                            .find_next_occurrence(&test_time, false)\n                            .expect(\"cron: a schedule should have a next event\");\n                    }));\n                    if let Err(_) = result {\n                        tracing::error!(\"A panic occurred while finding the next occurrence\");\n                        return Err(Error::BadRequest(format!(\n                            \"cron: a panic occurred during find_next_occurrence\"\n                        )));\n                    }\n\n                    if let Err(e) = result {\n                        tracing::error!(\n                            \"An error occurred while finding the next occurrence: {:?}\",\n                            e\n                        );\n                        return Err(Error::BadRequest(format!(\n                            \"cron: error during find_next_occurrence: {:?}\",\n                            e\n                        )));\n                    }\n                }\n","sourceCodeStart":1040,"sourceCodeEnd":1076,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-common/src/utils.rs#L1040-L1076","documentation":"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\").","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet schedule = \"0 0 30 2 *\"; // Feb 30 - no such date, croner may panic scanning forward\nSchedule::from_str(schedule)?;\n\n// after\nlet schedule = \"0 0 1 3 *\"; // use an existing date, or a supported expression\nSchedule::from_str(schedule)?;","handlingStrategy":"validation","validationCode":"// validate the schedule string before submitting it\nfn valid_schedule(s: &str) -> bool {\n    use cron::Schedule;\n    s.parse::<Schedule>()\n        .map(|sch| sch.upcoming(chrono_tz::UTC).next().is_some())\n        .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"// caller side: from_str returns Result, match it\nmatch Schedule::from_str(&input) {\n    Ok(s) => save_schedule(s),\n    Err(Error::BadRequest(msg)) => show_validation_error(msg),\n    Err(e) => return Err(e),\n}","preventionTips":["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."],"tags":["rust","cron","validation","panic"],"backgroundTag":"invalid-cron-expression","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}