temporalio/temporal · error

CronString has time zone but missing fields

Error message

CronString has time zone but missing fields

What it means

parseCronString supports an optional leading TZ= or CRON_TZ= prefix for cron strings. If the string begins with such a prefix but contains no space-separated fields after it (strings.Cut finds no space), there is no cron expression left to parse and this error is returned.

Source

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

		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, "=")
	}

	// split out comment
	c, comment, _ = strings.Cut(c, "#")
	c = strings.TrimSpace(c)
	comment = strings.TrimSpace(comment)

	// handle @every intervals
	if strings.HasPrefix(c, "@every") {
		iv, err := parseCronStringInterval(c)
		return nil, iv, "", err
	}

	// handle @hourly, etc.
	c = handlePredefinedCronStrings(c)

View on GitHub (pinned to bde624efd1)

Solutions

  1. Append the full 5-7 field cron expression after the timezone prefix, e.g. "TZ=America/New_York 0 9 * * *".
  2. Check the source config for truncation — quotes, newlines, or templating that swallowed the rest of the string.
  3. If the schedule doesn't need a custom zone, drop the TZ prefix entirely and rely on UTC/default.
  4. Validate the string by running it through parseCronString (or a cron linter) before persisting the schedule.

Example fix

// before
cronSpec := "CRON_TZ=Europe/Berlin"
// after
cronSpec := "CRON_TZ=Europe/Berlin 30 2 * * *"
Defensive patterns

Strategy: validation

Validate before calling

func validateTZCron(c string) error {
	c = strings.TrimSpace(c)
	if (strings.HasPrefix(c, "TZ=") || strings.HasPrefix(c, "CRON_TZ=")) && !strings.Contains(c, " ") {
		return errors.New("timezone prefix present but no cron fields")
	}
	return nil
}

Try / catch

if err != nil && strings.Contains(err.Error(), "CronString has time zone but missing fields") {
	// inspect config source for truncation, then resubmit full expression
}

Prevention

When it happens

Trigger: Passing a schedule/cron string like "TZ=America/New_York" or "CRON_TZ=UTC" with no trailing cron fields to a Schedule spec or canonicalizeSpec.

Common situations: Config value truncated by quoting/whitespace stripping in YAML/env; template rendering that dropped the cron expression; hand-edited schedule JSON where the expression part was accidentally deleted.

Related errors


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