temporalio/temporal · error
interval is too small
Error message
interval is too small
What it means
validateInterval requires the interval duration to be at least one second. Intervals below 1s would make the scheduler fire at sub-second frequency, which the schedule implementation does not support (and the TODO notes validation/capping is deliberately kept out of the workflow-based path to avoid non-determinism).
Source
Thrown at service/worker/scheduler/spec.go:263
if len(scs.Comment) > maxCommentLen {
errs = append(errs, "comment is too long")
}
if len(errs) > 0 {
return errors.New("invalid calendar spec: " + strings.Join(errs, ", "))
}
return nil
}
func validateInterval(i *schedulepb.IntervalSpec) error {
if i == nil {
return errors.New("interval is nil")
}
// TODO: use timestamp.ValidateAndCapProtoDuration after switching to state machine based implementation.
// Not adding it to workflow based implementation to avoid potential non-determinism errors.
iv, phase := timestamp.DurationValue(i.Interval), timestamp.DurationValue(i.Phase)
if iv < time.Second {
return errors.New("interval is too small")
} else if phase < 0 {
return errors.New("phase is negative")
} else if phase >= iv {
return errors.New("phase cannot be greater than Interval")
}
return nil
}
func (b *SpecBuilder) loadTimezone(spec *schedulepb.ScheduleSpec) (*time.Location, error) {
if spec.TimezoneData != nil {
return time.LoadLocationFromTZData(spec.TimezoneName, spec.TimezoneData)
}
if cached, ok := b.locationCache.Get(spec.TimezoneName).(*locationAndError); ok {
return cached.loc, cached.err
}
loc, err := time.LoadLocation(spec.TimezoneName)
b.locationCache.Put(spec.TimezoneName, &locationAndError{View on GitHub (pinned to bde624efd1)
Solutions
- Set the interval to at least 1 second, e.g. durationpb.New(5*time.Second).
- Use a worker-side timer/loop for sub-second periodicity instead of a Schedule.
- Check unit conversions in the code that builds the duration (ms vs s) before sending the update.
- Pre-validate the duration client-side and reject values < time.Second with a clear user-facing message.
Example fix
// before
iv := &schedulepb.IntervalSpec{Interval: durationpb.New(500 * time.Millisecond)}
// after
iv := &schedulepb.IntervalSpec{Interval: durationpb.New(5 * time.Second)} Defensive patterns
Strategy: validation
Validate before calling
d := iv.GetInterval().AsDuration()
if d < time.Second {
return fmt.Errorf("schedule interval %s below 1s minimum; use >= 1s", d)
} Prevention
- Remember schedules support a minimum interval of 1 second
- Do not confuse worker poll intervals (sub-second) with schedule intervals
- Audit duration unit conversions (ms/s) in spec-building code
- Use worker-side timers/loops for sub-second periodicity instead of Schedules
When it happens
Trigger: Submitting an IntervalSpec whose Interval duration is 0 or between 0 and 1 second (e.g. durationpb.New(500*time.Millisecond) or a zero-value durationpb), reached via canonicalizeSpec.
Common situations: Confusing Temporal worker poll/pollInterval style sub-second settings with schedule intervals; unit tests with millisecond intervals that pass in workflows but are rejected for schedules; a client sending seconds where milliseconds were intended (or vice versa) producing tiny values.
Related errors
- phase cannot be greater than Interval
- out of range
- conflicting timezone names
- ${errs}
- CronString has time zone but missing fields
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/8cd25346db4ba2fe.
Report an issue: GitHub.