temporalio/temporal · error
CronString does not have interval after @every
Error message
CronString does not have interval after @every
What it means
parseCronStringInterval handles the @every shorthand for cron strings in schedule specs. After matching an @every prefix, it splits the string on the first space to extract the duration; if there is no space, there is no interval value after @every, so it returns this error. It prevents a bare '@every' from silently producing an invalid or zero interval spec.
Source
Thrown at service/worker/scheduler/calendar.go:309
case 7:
cal.Second, cal.Minute, cal.Hour, cal.DayOfMonth, cal.Month, cal.DayOfWeek, cal.Year = toks[0], toks[1], toks[2], toks[3], toks[4], toks[5], toks[6]
default:
return nil, nil, "", errors.New("CronString does not have 5-7 fields")
}
structured, err := parseCalendarToStructured(&cal)
if err != nil {
return nil, nil, "", err
}
return structured, nil, tzName, nil
}
func parseCronStringInterval(c string) (*schedulepb.IntervalSpec, error) {
// split after @every
_, interval, found := strings.Cut(c, " ")
if !found {
return nil, errors.New("CronString does not have interval after @every")
}
// allow @every 14h/3h
interval, phase, _ := strings.Cut(interval, "/")
intervalDuration, err := timestamp.ParseDuration(interval)
if err != nil {
return nil, err
}
if phase == "" {
return &schedulepb.IntervalSpec{Interval: durationpb.New(intervalDuration)}, nil
}
phaseDuration, err := timestamp.ParseDuration(phase)
if err != nil {
return nil, err
}
return &schedulepb.IntervalSpec{Interval: durationpb.New(intervalDuration), Phase: durationpb.New(phaseDuration)}, nil
}
func handlePredefinedCronStrings(c string) string {View on GitHub (pinned to bde624efd1)
Solutions
- Append a valid duration after @every, e.g. '@every 1h30m' (durations parsed by timestamp.ParseDuration support h/m/s compounds and allow the phase form '@every 14h/3h').
- Validate the cron string before submitting: ensure it starts with '@every ' followed by a non-empty token.
- If a cron expression was intended instead of @every, use standard cron syntax (e.g. '0 */2 * * *') rather than the @every prefix.
- Trim and check user-supplied schedule strings at the UI/CLI layer to reject empty @every values early.
Example fix
// before cronStr := "@every" spec, err := parseCronString(cronStr) // error: CronString does not have interval after @every // after cronStr := "@every 5m" spec, err := parseCronString(cronStr)
Defensive patterns
Strategy: validation
Validate before calling
func validEveryCron(c string) bool {
if !strings.HasPrefix(c, "@every ") { return false }
return len(strings.TrimSpace(strings.TrimPrefix(c, "@every "))) > 0
}
// call parseCronString only if validEveryCron(cronStr) Prevention
- Always include a duration after @every (e.g. '@every 30m')
- Validate cron strings with a parser before persisting schedule configs
- Watch for templates/placeholders left unsubstituted in config files
- Prefer standard cron syntax if fixed periodicity at second granularity is not needed
When it happens
Trigger: Calling parseCronString (directly or via schedule spec creation) with a cron string like '@every' with nothing after it, or '@every' followed by only whitespace/newline so strings.Cut on " " finds no separator.
Common situations: Hand-edited schedule configs where the duration was accidentally deleted; templates with a placeholder ('@every {{interval}}') left unsubstituted; trailing whitespace trimmed away leaving a bare @every; copy-paste from docs that dropped the duration.
Related errors
- CronString has time zone but missing fields
- CronString does not have 5-7 fields
- %s has too many slashes
- %s missing step value
- %s has invalid Step
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/41cd2a62b4d800d9.
Report an issue: GitHub.