Skip to main content

Validation and Errors

This page lists every exception this library throws, explains what triggers each one, and describes how to fix it.

When Validation Happens

Validation is eager. Almost everything is caught the moment a value is assigned, rather than later when the expression is used.

StageWhat it checks
Property assignmentSyntax and numeric range for that one field. Every syntax error is caught here.
ToCronExpression()Additionally, that the day can occur in at least one selected month.
GetNextExecution()The same day/month check, plus the search horizon.

Because the constructor and every fluent helper assign through the properties, they all inherit the first stage. An invalid argument to OnHours(24) or EveryXMinutes(0) throws from that call, not from a later ToCronExpression().

WillRunOn() performs no validation of its own.

TryParse is the one entry point that never throws - it returns false and sets its out parameter to null for every failure below.

Exceptions

These are the exceptions the library can raise, ordered roughly by how often you are likely to encounter them:

ScenarioExceptionHow to fix
Non-numeric or malformed token (a, 1a, 1-2-3, 1/2/3)FormatExceptionCheck for a misspelled name or a repeated /. Names are only recognized on Month and DayOfWeek - see Month and Day-of-Week Names.
A parsed string without exactly five fields, or an unrecognized macro (@foo)FormatExceptionSupply all five fields, or use a recognized macro - see Macros. Extra spaces between fields are fine.
An empty field, such as OnHours() with no argumentsFormatExceptionGuard against passing an empty array.
Value outside the field's range (8 for DayOfWeek)ArgumentOutOfRangeExceptionSee the range column in Cron Expression Format. DayOfWeek allows 0-7.
Range start not less than end (6-3, 5-5)ArgumentOutOfRangeExceptionFor 5-5, assign the single value directly instead. For 6-3, you likely want a range that crosses the field's wraparound point (e.g. Saturday through Wednesday) - ranges can't wrap in any cron dialect, so split it into two ascending ranges joined by a comma, or list the individual values. See Ranges Don't Wrap.
Step interval of 0ArgumentOutOfRangeExceptionAn interval must be at least 1. Reported as Interval value 0 for <field> must be greater than 0.
Step interval larger than the field's maximum (*/60 on minute)ArgumentOutOfRangeExceptionUse an interval no greater than the field's own maximum.
Step syntax on DayOfWeek (*/1, 1/2)NotSupportedExceptionUse a list (1,3,5) or a range (1-5) instead.
The @reboot macroNotSupportedExceptionThere is no five-field expression for "run once at startup" - use your scheduler's native reboot/startup trigger instead of a cron expression. See Macros.
Day cannot occur in any selected month (day 30 with month 2)ArgumentOutOfRangeExceptionAdjust the day, or widen the month field to include one that has that day.
No match found within the search horizonCronSearchHorizonExceededExceptionShould not happen for a satisfiable expression; see the search horizon.

Deferred Day and Month Checks

The day/month check is the one rule that does not run on assignment, because it depends on two fields at once. It runs when ToCronExpression() or GetNextExecution() is called, for any Day value other than the wildcard * - a single value, a list, a range, and a step are all expanded into the full set of days they select and checked against the selected months:

Day field is…Set byCheck runs?
*EveryDay(), Day = "*"No
Anything else - a single value, list, range, or stepOnDays(15), RangeOfDays(1, 15), EveryXDays(2), Day = "30,31"Yes

The combination fails only when no selected day can occur in any selected month, so a single valid day anywhere in the selection is enough to pass. That's why RangeOfDays(29, 31) combined with February does not throw: 29 is a valid day in February, which this library treats as having 29 days (see Valid Day and Month Pairs), even though 30 and 31 are not. OnDays(30) with February does throw, since its only selected day never occurs there. See Field semantics for what the check actually verifies.