temporalio/temporal · error
invalid calendar spec
Error message
invalid calendar spec: ${errs} What it means
validateStructuredCalendar collects all validation failures for a schedulepb.CalendarSpec (required fields, oversized comments, out-of-range second/minute/hour values, etc.) and joins them into a single message. This error means the submitted calendar spec structurally failed one or more of those checks and was rejected before the schedule could be compiled or canonicalized.
Solutions
- Read the joined field list in the error message and fix each listed field (e.g. seconds 0-59, minutes 0-59, hours 0-23).
- Shorten the Comment field to under maxCommentLen.
- Construct specs via the scheduler's high-level ScheduleSpec/calendar helpers instead of raw protobuf structs so defaults are applied.
- Validate spec fields client-side (ranges, required fields) before submitting the UpdateSchedule request.
Example fix
// before
spec := &schedulepb.CalendarSpec{Second: "61", Comment: strings.Repeat("x", 600)}
// after
spec := &schedulepb.CalendarSpec{Second: "0", Minute: "*/15", Hour: "*", Comment: "every 15 min"} Defensive patterns
Strategy: validation
Validate before calling
func validCalendar(scs *schedulepb.CalendarSpec) error {
var errs []string
if n, err := strconv.Atoi(scs.GetSecond()); err != nil || n < 0 || n > 59 { errs = append(errs, "second out of range") }
if n, err := strconv.Atoi(scs.GetMinute()); err != nil || n < 0 || n > 59 { errs = append(errs, "minute out of range") }
if n, err := strconv.Atoi(scs.GetHour()); err != nil || n < 0 || n > 23 { errs = append(errs, "hour out of range") }
if len(scs.GetComment()) > 256 { errs = append(errs, "comment too long") }
if len(errs) > 0 { return fmt.Errorf("invalid calendar spec: %s", strings.Join(errs, ", ")) }
return nil
} Prevention
- Build specs via high-level ScheduleSpec helpers, not raw protobuf structs
- Clamp/range-check user-entered time fields in forms and CLIs
- Keep the Comment field short (it is metadata, not a log)
- Validate specs in unit tests before deploying schedule-creating code
When it happens
Trigger: Calling mustCompileCalendarSpec or canonicalizeSpec with a *schedulepb.CalendarSpec that has invalid or missing field values — e.g. second=61, missing required field, or a Comment longer than maxCommentLen.
Common situations: Building calendar specs by hand in Go without using the high-level ScheduleSpec helpers; migrating specs from cron strings where ranges were mis-translated; UI forms that don't clamp minute/hour inputs; long operator notes stuffed into Comment.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- ${errs}
- out of range
- conflicting timezone names
- CronString does not have 5-7 fields
- CronString does not have interval after @every
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/0d2837cc55e6862d.
Report an issue: GitHub.
Appendix: source
Thrown at service/worker/scheduler/spec.go:250
errs = append(errs, fmt.Sprintf("%s has invalid Step", field))
}
}
}
checkRanges(scs.Second, "Second", 0, 59)
checkRanges(scs.Minute, "Minute", 0, 59)
checkRanges(scs.Hour, "Hour", 0, 23)
checkRanges(scs.DayOfMonth, "DayOfMonth", 1, 31)
checkRanges(scs.Month, "Month", 1, 12)
checkRanges(scs.Year, "Year", minCalendarYear, maxCalendarYear)
checkRanges(scs.DayOfWeek, "DayOfWeek", 0, 6)
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")
}View on GitHub (pinned to bde624efd1)