temporalio/temporal · error
invalid calendar spec: ${errs}
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.
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)
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
- out of range
- ${errs}
- conflicting timezone names
- CronString has time zone but missing fields
- CronString does not have 5-7 fields
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/0d2837cc55e6862d.
Report an issue: GitHub.