{"record":{"id":"2fbcb88f2bc18c27","repo":"hashicorp/nomad","slug":"s-can-t-parse-time-duration-s","errorCode":null,"errorMessage":"%s can't parse time duration %s","messagePattern":"(.+?) can't parse time duration (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"command/agent/config_parse.go","lineNumber":303,"sourceCode":"}\n\n// durationConversionMap holds args for one duration conversion\ntype durationConversionMap struct {\n\ttargetFieldPath string\n\ttargetField     *time.Duration\n\tsourceField     *string\n\tsetFunc         func(*time.Duration)\n}\n\n// convertDurations parses the duration strings specified in the config files\n// into time.Durations\nfunc convertDurations(xs []durationConversionMap) error {\n\tfor _, x := range xs {\n\t\t// if targetField is not a pointer itself, use the field map.\n\t\tif x.targetField != nil && x.sourceField != nil && \"\" != *x.sourceField {\n\t\t\td, err := time.ParseDuration(*x.sourceField)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"%s can't parse time duration %s\", x.targetFieldPath, *x.sourceField)\n\t\t\t}\n\n\t\t\t*x.targetField = d\n\t\t} else if x.setFunc != nil && x.sourceField != nil && \"\" != *x.sourceField {\n\t\t\t// if targetField is a pointer itself, use the setFunc closure.\n\t\t\td, err := time.ParseDuration(*x.sourceField)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"%s can't parse time duration %s\", x.targetFieldPath, *x.sourceField)\n\t\t\t}\n\t\t\tx.setFunc(&d)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc extraKeys(c *Config) error {\n\t// hcl leaves behind extra keys when parsing JSON. These keys","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/command/agent/config_parse.go#L285-L321","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\ngc_interval = 30\n\n// after\ngc_interval = \"30s\"","handlingStrategy":"validation","validationCode":"var durationRe = regexp.MustCompile(`^-?\\d+(\\.\\d+)?(ns|us|µs|ms|s|m|h)$`)\nfunc validateDurationFields(cfg map[string]any, keys ...string) error {\n\tfor _, k := range keys {\n\t\tv, ok := cfg[k].(string)\n\t\tif !ok || !durationRe.MatchString(v) {\n\t\t\treturn fmt.Errorf(\"%s = %v is not a valid Go duration\", k, cfg[k])\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":"func isParsableDuration(s string) bool {\n\t_, err := time.ParseDuration(s)\n\treturn err == nil\n}","tryCatchPattern":"cfg, err := ParseConfigFile(path)\nif err != nil {\n\tif strings.Contains(err.Error(), \"can't parse time duration\") {\n\t\treturn fmt.Errorf(\"fix duration value in %s (units: ns/us/ms/s/m/h): %w\", path, err)\n\t}\n\treturn err\n}","preventionTips":["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"],"tags":["config-parsing","duration","go"],"backgroundTag":"invalid-duration-format","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}