HangfireIO/Hangfire · error · CronFormatException
Wrong number of parts in the `{cronExpression}` cron express
Error message
Wrong number of parts in the `{cronExpression}` cron expression, you can only use 5 or 6 (with seconds) part-based expressions. What it means
Thrown by RecurringJobEntity.ParseCronExpression when a cron expression that doesn't start with '@' does not split into exactly 5 or 6 (with seconds) whitespace-separated parts. Hangfire uses Cronos and requires standard 5-field or 6-field-with-seconds expressions. This is a CronFormatException surfaced before the expression is handed to Cronos.
Source
Thrown at src/Hangfire.Core/RecurringJobEntity.cs:300
return String.Join(";", _recurringJob.Select(static x => $"{x.Key}:{x.Value}"));
}
public static CronExpression ParseCronExpression([NotNull] string cronExpression)
{
if (cronExpression == null) throw new ArgumentNullException(nameof(cronExpression));
var format = CronFormat.Standard;
if (!cronExpression.StartsWith("@", StringComparison.OrdinalIgnoreCase))
{
var parts = cronExpression.Split(SeparatorCharacters, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 6)
{
format |= CronFormat.IncludeSeconds;
}
else if (parts.Length != 5)
{
throw new CronFormatException(
$"Wrong number of parts in the `{cronExpression}` cron expression, you can only use 5 or 6 (with seconds) part-based expressions.");
}
}
return CronExpression.Parse(cronExpression, format);
}
}
}View on GitHub (pinned to c236dd0f93)
Solutions
- Use exactly 5 fields (min hour day month weekday) or 6 fields when seconds are needed.
- Validate the cron string with a unit test or a cron validator before calling AddOrUpdate.
- If you need named macros, use '@daily', '@hourly', etc., which bypass the part-count check.
Example fix
// before
RecurringJob.AddOrUpdate("j", () => Work(), "0 12 * *"); // 4 fields
// after
RecurringJob.AddOrUpdate("j", () => Work(), "0 12 * * *"); // 5 fields Defensive patterns
Strategy: validation
Validate before calling
static bool IsValidCronParts(string cron)
{
if (string.IsNullOrEmpty(cron) || cron.StartsWith("@")) return cron?.StartsWith("@") == true;
var parts = cron.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
return parts.Length == 5 || parts.Length == 6;
} Try / catch
try { RecurringJob.AddOrUpdate(id, () => Work(), cron); }
catch (CronFormatException ex) { /* fix cron string */ } Prevention
- Use 5-field cron by default; add a 6th only when seconds are required.
- Validate cron strings with a unit test covering all scheduled jobs.
- Prefer Cron.* helpers or @macros to avoid hand-crafted field counts.
When it happens
Trigger: Passing a cron string with 4 fields (e.g. '* * * *'), 7 fields, empty string, or a malformed schedule. Happens during AddOrUpdate validation and during scheduling when the stored Cron is read.
Common situations: Quartz-style 7-field expressions mistakenly used. Extra/missing spaces or trailing tokens. Copying a cron from a system that uses a different field count. Empty or whitespace-only cron from a misconfigured setting.
Related errors
- CRON expression is invalid. Please see the inner exception f
- Invalid Cron Expression
- cronExpression
- Attempts value must be equal or greater than zero.
- DelaysInSeconds value must be an array of non-negative numbe
AI-assisted analysis of HangfireIO/Hangfire@c236dd0f93 (2026-08-13).
Data as JSON: /api/errors/0be03723edaf417d.
Report an issue: GitHub.