temporalio/temporal · error

${errs}

Error message

${errs}

What it means

parseCalendarToStructured converts a CalendarSpec into a StructuredCalendarSpec by parsing each field independently; errors from every field are collected and joined with commas into a single error listing all offending fields (e.g. "DayOfMonth: ... out of range, Month: ..."). It fires when any of Second/Minute/Hour/DayOfWeek/DayOfMonth/Month/Year is malformed or out of range.

Source

Thrown at service/worker/scheduler/calendar.go:236

	makeRangeOrNil := func(s, field, def string, minVal, maxVal int, parseMode parseMode) []*schedulepb.Range {
		r, err := makeRange(s, field, def, minVal, maxVal, parseMode)
		if err != nil {
			errs = append(errs, err.Error())
		}
		return r
	}
	ss := &schedulepb.StructuredCalendarSpec{
		Second:     makeRangeOrNil(cal.Second, "Second", "0", 0, 59, parseModeInt),
		Minute:     makeRangeOrNil(cal.Minute, "Minute", "0", 0, 59, parseModeInt),
		Hour:       makeRangeOrNil(cal.Hour, "Hour", "0", 0, 23, parseModeInt),
		DayOfWeek:  makeRangeOrNil(cal.DayOfWeek, "DayOfWeek", "*", 0, 7, parseModeDow),
		DayOfMonth: makeRangeOrNil(cal.DayOfMonth, "DayOfMonth", "*", 1, 31, parseModeInt),
		Month:      makeRangeOrNil(cal.Month, "Month", "*", 1, 12, parseModeMonth),
		Year:       makeRangeOrNil(cal.Year, "Year", "*", minCalendarYear, maxCalendarYear, parseModeYear),
		Comment:    cal.Comment,
	}
	if len(errs) > 0 {
		return nil, errors.New(strings.Join(errs, ", "))
	}
	return ss, nil
}

func parseCronString(c string) (*schedulepb.StructuredCalendarSpec, *schedulepb.IntervalSpec, string, error) {
	var tzName string
	var comment string

	c = strings.TrimSpace(c)

	// split out timezone
	if strings.HasPrefix(c, "TZ=") || strings.HasPrefix(c, "CRON_TZ=") {
		tz, rest, found := strings.Cut(c, " ")
		if !found {
			return nil, nil, "", errors.New("CronString has time zone but missing fields")
		}
		c = rest
		_, tzName, _ = strings.Cut(tz, "=")

View on GitHub (pinned to bde624efd1)

Solutions

  1. Parse the joined message: each comma-separated segment names the failing field; fix each one's value/expression.
  2. Validate ranges before constructing the spec (Second 0-59, Minute 0-59, Hour 0-23, DayOfMonth 1-31, Month 1-12, DayOfWeek 0-7, Year in supported range).
  3. Use '*' for fields you don't intend to constrain instead of numeric guesses.
  4. Test the spec locally with parseCalendarToStructured/mustCompileCalendarSpec or a unit test before applying to production schedules.

Example fix

// before
cal.Month = "13"
cal.DayOfWeek = "8"
// after
cal.Month = "1-12"
cal.DayOfWeek = "0-6"
Defensive patterns

Strategy: validation

Validate before calling

var fieldBounds = map[string][2]int{
	"Second": {0, 59}, "Minute": {0, 59}, "Hour": {0, 23},
	"DayOfMonth": {1, 31}, "Month": {1, 12}, "DayOfWeek": {0, 7},
}

Try / catch

ss, err := parseCalendarToStructured(cal)
if err != nil {
	for _, part := range strings.Split(err.Error(), ", ") {
		// each part names one invalid field; fix and retry
	}
}

Prevention

When it happens

Trigger: Creating a schedule with a CalendarSpec whose fields include invalid values or unparsable expressions (bad ranges, unknown month/day names, values outside each field's bounds).

Common situations: Constructing CalendarSpec protobufs programmatically with uninitialized or wrong-range fields; validating user-supplied cron strings via the scheduler; copies of legacy specs after schema/version changes to field formats.

Related errors


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