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
- Correct the timezone name to a valid IANA identifier (e.g. America/New_York, UTC)
- Install tzdata in the deployment image (apk add tzdata, apt install tzdata)
- Import _ "time/tzdata" in Go to embed the timezone database in the binary
- 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
- Validate timezone names against time.LoadLocation at startup
- Install tzdata or import _ "time/tzdata" for containers
- Restrict user-supplied timezone input to a whitelist
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
- multiple optionals may not be configured
- parser does not accept descriptors
- empty spec string
- expected exactly fields, found
- expected to fields, found
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)