hashicorp/nomad · error
%s can't parse time duration %s
Error message
%s can't parse time duration %s
What it means
convertDurations converts HCL string fields (like gc_interval) into time.Duration values on the config struct. For fields with a direct pointer target, time.ParseDuration is called on the source string; this error reports the field name and the invalid value when parsing fails.
Source
Thrown at command/agent/config_parse.go:303
}
// durationConversionMap holds args for one duration conversion
type durationConversionMap struct {
targetFieldPath string
targetField *time.Duration
sourceField *string
setFunc func(*time.Duration)
}
// convertDurations parses the duration strings specified in the config files
// into time.Durations
func convertDurations(xs []durationConversionMap) error {
for _, x := range xs {
// if targetField is not a pointer itself, use the field map.
if x.targetField != nil && x.sourceField != nil && "" != *x.sourceField {
d, err := time.ParseDuration(*x.sourceField)
if err != nil {
return fmt.Errorf("%s can't parse time duration %s", x.targetFieldPath, *x.sourceField)
}
*x.targetField = d
} else if x.setFunc != nil && x.sourceField != nil && "" != *x.sourceField {
// if targetField is a pointer itself, use the setFunc closure.
d, err := time.ParseDuration(*x.sourceField)
if err != nil {
return fmt.Errorf("%s can't parse time duration %s", x.targetFieldPath, *x.sourceField)
}
x.setFunc(&d)
}
}
return nil
}
func extraKeys(c *Config) error {
// hcl leaves behind extra keys when parsing JSON. These keysView on GitHub (pinned to 482b49bf1a)
Solutions
- Add a valid Go duration unit: `gc_interval = "30s"`, `"5m"`, `"1h"`, etc.
- Use exactly the units Go supports: ns, us, ms, s, m, h (not min/hr/d)
- Check the field path named in the error message and fix that specific value
- Quote the value in HCL so it is parsed as a string with units, not a number
Example fix
// before gc_interval = 30 // after gc_interval = "30s"
Defensive patterns
Strategy: validation
Validate before calling
var durationRe = regexp.MustCompile(`^-?\d+(\.\d+)?(ns|us|µs|ms|s|m|h)$`)
func validateDurationFields(cfg map[string]any, keys ...string) error {
for _, k := range keys {
v, ok := cfg[k].(string)
if !ok || !durationRe.MatchString(v) {
return fmt.Errorf("%s = %v is not a valid Go duration", k, cfg[k])
}
}
return nil
} Type guard
func isParsableDuration(s string) bool {
_, err := time.ParseDuration(s)
return err == nil
} Try / catch
cfg, err := ParseConfigFile(path)
if err != nil {
if strings.Contains(err.Error(), "can't parse time duration") {
return fmt.Errorf("fix duration value in %s (units: ns/us/ms/s/m/h): %w", path, err)
}
return err
} Prevention
- Always include a Go duration unit: s, m, h (never bare numbers, min, hr, days)
- Quote duration values in HCL so they stay strings
- Pre-validate user/template-supplied durations with time.ParseDuration
- Add a config lint step in CI catching unparsable durations
When it happens
Trigger: ParseConfigFile on a config where a duration field (e.g. gc_interval, or any entry in the durationConversionMap list) is set to a string not accepted by time.ParseDuration — e.g. "5", "five minutes", "1hr", or an empty-ish/whitespace value that still passes the != "" check.
Common situations: Writing `gc_interval = 30` (a bare number, no unit) instead of `gc_interval = "30s"`; using units Go doesn't understand like "min" or "hr"; values from templates rendering without units.
Understand the failure class
Background: "invalid duration" / "failed to parse duration": why your timeout, interval, or TTL string is rejected and which formats each library accepts — this error's family across 32 libraries.
Related errors
- error parsing HTTPMaxSize: %w
- error parsing GCSTimeout: %w
- error parsing GitTimeout: %w
- error parsing HgTimeout: %w
- error parsing S3Timeout: %w
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/2fbcb88f2bc18c27.
Report an issue: GitHub.