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

  1. Set the interval to at least 1 second, e.g. durationpb.New(5*time.Second).
  2. Use a worker-side timer/loop for sub-second periodicity instead of a Schedule.
  3. Check unit conversions in the code that builds the duration (ms vs s) before sending the update.
  4. 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

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


AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01). Data as JSON: /api/errors/8cd25346db4ba2fe. Report an issue: GitHub.