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

  1. 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).
  2. Shorten the Comment field to under maxCommentLen.
  3. Construct specs via the scheduler's high-level ScheduleSpec/calendar helpers instead of raw protobuf structs so defaults are applied.
  4. 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

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


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