robfig/cron · error

provided bad location

Error message

provided bad location %s: %v

What it means

When the spec starts with TZ= or CRON_TZ=, Parse extracts the location name between '=' and the first space and loads it with time.LoadLocation. If the name is not a valid IANA timezone (or tzdata is unavailable), the parse fails with this formatted error.

Solutions

  1. Correct the timezone name to a valid IANA identifier (e.g. America/New_York, UTC)
  2. Install tzdata in the deployment image (apk add tzdata, apt install tzdata)
  3. Import _ "time/tzdata" in Go to embed the timezone database in the binary
  4. Validate the location with time.LoadLocation before calling Parse

Example fix

// before
sched, _ := parser.Parse("TZ=Pacific/Ocean * * * * *")
// after
if _, err := time.LoadLocation("Pacific/Auckland"); err == nil {
    sched, _ = parser.Parse("TZ=Pacific/Auckland * * * * *")
}
Defensive patterns

Strategy: validation

Validate before calling

if i := strings.Index(spec, "="); strings.HasPrefix(spec, "TZ=") || strings.HasPrefix(spec, "CRON_TZ=") {
	if _, err := time.LoadLocation(spec[i+1:strings.Index(spec, " ")]); err != nil {
		return fmt.Errorf("unknown timezone: %w", err)
	}
}

Try / catch

sched, err := parser.Parse(spec)
var locErr *time.ParseError
if errors.As(err, nil) || strings.Contains(err.Error(), "bad location") {
	return fmt.Errorf("check TZ= in cron spec: %w", err)
}

Prevention

When it happens

Trigger: Parsing specs like "TZ=Bad/Zone * * * *" or "CRON_TZ=UTCX 0 0 * * *" where the location name is misspelled or unknown.

Common situations: Typo in timezone name in a cron config; deploying to a minimal Docker image (scratch/alpine) without tzdata so even valid names fail; building without importing time/tzdata.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at parser.go:100

	return Parser{options}
}

// Parse returns a new crontab schedule representing the given spec.
// It returns a descriptive error if the spec is not valid.
// It accepts crontab specs and features configured by NewParser.
func (p Parser) Parse(spec string) (Schedule, error) {
	if len(spec) == 0 {
		return nil, fmt.Errorf("empty spec string")
	}

	// Extract timezone if present
	var loc = time.Local
	if strings.HasPrefix(spec, "TZ=") || strings.HasPrefix(spec, "CRON_TZ=") {
		var err error
		i := strings.Index(spec, " ")
		eq := strings.Index(spec, "=")
		if loc, err = time.LoadLocation(spec[eq+1 : i]); err != nil {
			return nil, fmt.Errorf("provided bad location %s: %v", spec[eq+1:i], err)
		}
		spec = strings.TrimSpace(spec[i:])
	}

	// Handle named schedules (descriptors), if configured
	if strings.HasPrefix(spec, "@") {
		if p.options&Descriptor == 0 {
			return nil, fmt.Errorf("parser does not accept descriptors: %v", spec)
		}
		return parseDescriptor(spec, loc)
	}

	// Split on whitespace.
	fields := strings.Fields(spec)

	// Validate & fill in any omitted or optional fields
	var err error
	fields, err = normalizeFields(fields, p.options)

View on GitHub (pinned to bc59245fe1)