robfig/cron · error

expected exactly fields, found

Error message

expected exactly %d fields, found %d: %s

What it means

normalizeFields validates that the whitespace-split spec has exactly the number of fields the configured options require. When the parser has no optional fields, min==max, and a field count other than that exact number fails with this error listing expected, found, and the raw fields.

Solutions

  1. Adjust the spec to the field count the parser expects (add/remove a seconds field)
  2. Reconfigure NewParser options to match the field count of your specs (enable SecondOptional for both 5/6 fields)
  3. Normalize the spec (pad defaults) before calling Parse

Example fix

// before
p := cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)
s, err := p.Parse("0 5 * * 1") // 5 fields, parser wants 6
// after
s, err := p.Parse("0 0 5 * * 1") // seconds added
// or
p := cron.NewParser(cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)
Defensive patterns

Strategy: validation

Validate before calling

fields := strings.Fields(spec)
if len(fields) != 5 && len(fields) != 6 {
	return fmt.Errorf("cron spec must have 5 or 6 fields, got %d", len(fields))
}

Try / catch

sched, err := parser.Parse(spec)
if err != nil {
	return fmt.Errorf("invalid cron spec %q: %w", spec, err)
}

Prevention

When it happens

Trigger: Parsing a 5-field spec with a Parser configured for 6 fields (Second enabled) or vice versa; passing "* * * *" (4 fields) to a standard 5-field parser.

Common situations: Copying Linux crontab 5-field expressions into a seconds-enabled parser; omitting the day-of-month or month field by mistake; specs built by joining user inputs where empty parts collapse.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of robfig/cron@bc59245fe1 (2026-09-07). Data as JSON: /api/errors/1ce687e1a60b674b. Report an issue: GitHub.

Appendix: source

Thrown at parser.go:187

		optionals++
	}
	if optionals > 1 {
		return nil, fmt.Errorf("multiple optionals may not be configured")
	}

	// Figure out how many fields we need
	max := 0
	for _, place := range places {
		if options&place > 0 {
			max++
		}
	}
	min := max - optionals

	// Validate number of fields
	if count := len(fields); count < min || count > max {
		if min == max {
			return nil, fmt.Errorf("expected exactly %d fields, found %d: %s", min, count, fields)
		}
		return nil, fmt.Errorf("expected %d to %d fields, found %d: %s", min, max, count, fields)
	}

	// Populate the optional field if not provided
	if min < max && len(fields) == min {
		switch {
		case options&DowOptional > 0:
			fields = append(fields, defaults[5]) // TODO: improve access to default
		case options&SecondOptional > 0:
			fields = append([]string{defaults[0]}, fields...)
		default:
			return nil, fmt.Errorf("unknown optional field")
		}
	}

	// Populate all fields not part of options with their defaults
	n := 0

View on GitHub (pinned to bc59245fe1)