temporalio/temporal · error

interval is not a valid duration: %w

Error message

interval is not a valid duration: %w

What it means

Returned when an interval spec's `interval` duration in a ScheduleSpec fails protobuf timestamp/duration CheckValid() (e.g. nil semantics, negative, or malformed wire data). Temporal validates all interval spec fields server-side before accepting schedule creation/update to prevent undefined scheduler behavior.

Source

Thrown at service/frontend/workflow_handler.go:7016

	sizeLimitWarn := wh.config.BlobSizeLimitWarn(namespaceName)
	return common.CheckEventBlobSizeLimit(
		payloadSize,
		sizeLimitWarn,
		sizeLimitError,
		namespaceName,
		scheduleID,
		"",
		wh.metricsScope(ctx).WithTags(metrics.CommandTypeTag(enumspb.COMMAND_TYPE_UNSPECIFIED.String())),
		wh.throttledLogger,
		operation,
	)
}

func validateScheduleIntervalDurations(spec *schedulepb.ScheduleSpec) error {
	for _, interval := range spec.GetInterval() {
		if d := interval.GetInterval(); d != nil {
			if err := d.CheckValid(); err != nil {
				return fmt.Errorf("interval is not a valid duration: %w", err)
			}
		}
		if d := interval.GetPhase(); d != nil {
			if err := d.CheckValid(); err != nil {
				return fmt.Errorf("phase is not a valid duration: %w", err)
			}
		}
	}
	return nil
}

func validateScheduleTimestamps(spec *schedulepb.ScheduleSpec) error {
	if err := validateTimestamp(spec.GetStartTime(), "start time"); err != nil {
		return err
	}
	return validateTimestamp(spec.GetEndTime(), "end time")
}

View on GitHub (pinned to bde624efd1)

Solutions

  1. Ensure each interval duration is > 0 and has consistent seconds/nanoseconds (0 <= nanos < 1e9)
  2. Use durationpb.New(time.Duration) instead of hand-building durationpb.Duration
  3. Check for negative or overflowed values when converting from config strings
  4. Use an SDK typed duration type (e.g. Go time.Duration) so the client serializes correctly

Example fix

// before
interval := &schedulepb.IntervalSpec{Interval: &durationpb.Duration{Seconds: -3600}}
// after
interval := &schedulepb.IntervalSpec{Interval: durationpb.New(1 * time.Hour)}
Defensive patterns

Strategy: validation

Validate before calling

func validInterval(d *durationpb.Duration) bool { return d != nil && d.CheckValid() == nil && d.AsDuration() > 0 }

Type guard

func isDurationpb(v interface{}) *durationpb.Duration { d, ok := v.(*durationpb.Duration); return d }

Try / catch

err := validateScheduleIntervalDurations(spec); if err != nil { return serviceerror.NewInvalidArgumentf(...) }

Prevention

When it happens

Trigger: Calling CreateScheduleAsync or UpdateScheduleAsync (via ValidateScheduleSpec → validateScheduleIntervalDurations) with a ScheduleSpec whose Interval[*].Interval is a zero-value or invalid durationpb.Duration (seconds/nanos inconsistent, negative).

Common situations: Constructing durationpb.Duration manually with wrong sign; decoding corrupted protobuf; deserializing specs from config where seconds/nanos were miscomputed; SDK users passing nil-checked but semantically invalid durations.

Related errors


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