temporalio/temporal · error
phase is not a valid duration: %w
Error message
phase is not a valid duration: %w
What it means
Returned when the `phase` field of an IntervalSpec in a ScheduleSpec fails duration CheckValid(). Phase offsets the interval schedule within each interval and must be a valid, non-negative protobuf duration; Temporal validates it server-side to keep interval arithmetic well-defined.
Source
Thrown at service/frontend/workflow_handler.go:7021
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")
}
func validateTimestamp(value *timestamppb.Timestamp, field string) error {
if value != nil {
if err := value.CheckValid(); err != nil {
return fmt.Errorf("%s is not a valid timestamp: %w", field, err)
}View on GitHub (pinned to bde624efd1)
Solutions
- Ensure phase is >= 0 and < the interval duration
- Build phase with durationpb.New(time.Duration)
- Validate spec client-side before submitting the schedule request
Example fix
// before
spec := &schedulepb.ScheduleSpec{Interval: []*schedulepb.IntervalSpec{{Interval: durationpb.New(time.Hour), Phase: &durationpb.Duration{Nanos: -1}}}}
// after
spec := &schedulepb.ScheduleSpec{Interval: []*schedulepb.IntervalSpec{{Interval: durationpb.New(time.Hour), Phase: durationpb.New(30 * time.Minute)}}} Defensive patterns
Strategy: validation
Validate before calling
func validPhase(p *durationpb.Duration, interval *durationpb.Duration) bool { return p == nil || (p.CheckValid() == nil && p.AsDuration() >= 0 && p.AsDuration() < interval.AsDuration()) } Type guard
func hasValidPhase(is *schedulepb.IntervalSpec) bool { return is.GetPhase() == nil || is.GetPhase().CheckValid() == nil } Try / catch
if err := validateScheduleIntervalDurations(spec); err != nil { return err } Prevention
- Keep phase >= 0 and less than the interval
- Use durationpb.New rather than struct literals
- Validate before submitting schedule updates
When it happens
Trigger: CreateSchedule/UpdateSchedule with a ScheduleSpec whose Interval[*].Phase is negative or carries invalid seconds/nanos values, surfacing through validateScheduleIntervalDurations.
Common situations: Hand-constructed durationpb with negative nanos; copying phase from a misparsed config; version mismatches where an older client sends a malformed phase.
Related errors
- interval is not a valid duration: %w
- %s is not a valid timestamp: %w
- %s has unsupported overlap policy %v
- remaining actions cannot be negative
- batchParams is nil
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/65b6ce8ba562b590.
Report an issue: GitHub.