temporalio/temporal · error

%s Start is not in range [%d-%d]

Error message

%s Start is not in range [%d-%d]

What it means

makeRange parses one cron-style field (e.g. day-of-week) into a numeric range. When the part contains a dash (a range like '5-10'), parseValue validates the start against [minVal,maxVal]; if it fails (out-of-range value, or a value that fails the field's parse mode, e.g. non-numeric text where a name is not allowed), this error is returned naming the field and allowed bounds.

Source

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

			}
			hasStep = true
		}

		start, end := minVal, maxVal
		if part != "*" {
			if strings.Contains(part, "-") {
				// Only a single dash is allowed to denote a range (e.g. "1-5").
				// Inputs with multiple dashes like "1-5-7" should raise the
				// canonical "too many dashes" error expected by tests.
				if strings.Count(part, "-") > 1 { // no negative numbers are expected in spec
					return nil, fmt.Errorf("%s has too many dashes", field)
				}
				rangeParts := strings.SplitN(part, "-", 2)
				if len(rangeParts) != 2 {
					return nil, fmt.Errorf("%s has too many dashes", field)
				}
				if start, err = parseValue(rangeParts[0], minVal, maxVal, parseMode); err != nil {
					return nil, fmt.Errorf("%s Start is not in range [%d-%d]", field, minVal, maxVal)
				}
				if end, err = parseValue(rangeParts[1], start, maxVal, parseMode); err != nil {
					return nil, fmt.Errorf("%s End is before Start or not in range [%d-%d]", field, minVal, maxVal)
				}
			} else {
				if start, err = parseValue(part, minVal, maxVal, parseMode); err != nil {
					return nil, fmt.Errorf("%s is not in range [%d-%d]", field, minVal, maxVal)
				}
				if !hasStep {
					// if / is present, a single value is treated as that value to the
					// end. otherwise a single value is just the single value.
					end = start
				}
			}
		}
		// Special handling for Sunday: Turn "7" into "0", which may require an extra range.
		// Consider some cases:
		// 0-7 or 1-7   can turn into 0-6

View on GitHub (pinned to bde624efd1)

Solutions

  1. Check the start value of the range is within the printed [min-max] bounds for that field and fix it
  2. If using textual names, switch to a spec type that accepts names (e.g. CalendarSpec dayOfWeek strings) or use numeric values
  3. Catch the error and surface it as a user-facing schedule-validation message before creating the Schedule

Example fix

// before
calendar := &schedule.CalendarSpec{Hour: "25-30"}
// after
calendar := &schedule.CalendarSpec{Hour: "0-23"}
Defensive patterns

Strategy: validation

Validate before calling

func validCronRangeStart(part string, min, max int) error {
    i := strings.Index(part, "-")
    if i <= 0 { return nil }
    v, err := strconv.Atoi(part[:i])
    if err != nil || v < min || v > max {
        return fmt.Errorf("range start %q must be %d-%d", part[:i], min, max)
    }
    return nil
}

Prevention

When it happens

Trigger: Calling makeRange (via calendar spec parsing in a Scheduler/CalendarSpec workflow) with a range expression whose start bound is invalid, e.g. 'mon-sun' where month names are not accepted for that field, '60-70' for a minutes field (0-59), or a negative/out-of-bounds number like '99-100'.

Common situations: Hand-written cron-like schedule strings with typos; copying a cron expression between fields with different bounds (hour vs day-of-month); using textual names (MON, JAN) in fields that only accept numbers under the current parse mode.

Related errors


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