{"record":{"id":"ecb0b5f47a3e9028","repo":"hashicorp/nomad","slug":"http-read-timeout-not-a-valid-duration-w","errorCode":null,"errorMessage":"http_read_timeout not a valid duration: %w","messagePattern":"http_read_timeout not a valid duration: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/config/artifact.go","lineNumber":170,"sourceCode":"\t\treturn false\n\tcase !helper.SliceSetEq(a.FilesystemIsolationExtraPaths, o.FilesystemIsolationExtraPaths):\n\t\treturn false\n\tcase !pointer.Eq(a.SetEnvironmentVariables, o.SetEnvironmentVariables):\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (a *ArtifactConfig) Validate() error {\n\tif a == nil {\n\t\treturn fmt.Errorf(\"artifact must not be nil\")\n\t}\n\n\tif a.HTTPReadTimeout == nil {\n\t\treturn fmt.Errorf(\"http_read_timeout must be set\")\n\t}\n\tif v, err := time.ParseDuration(*a.HTTPReadTimeout); err != nil {\n\t\treturn fmt.Errorf(\"http_read_timeout not a valid duration: %w\", err)\n\t} else if v < 0 {\n\t\treturn fmt.Errorf(\"http_read_timeout must be > 0\")\n\t}\n\n\tif a.HTTPMaxSize == nil {\n\t\treturn fmt.Errorf(\"http_max_size must be set\")\n\t}\n\tif v, err := humanize.ParseBytes(*a.HTTPMaxSize); err != nil {\n\t\treturn fmt.Errorf(\"http_max_size not a valid size: %w\", err)\n\t} else if v > math.MaxInt64 {\n\t\treturn fmt.Errorf(\"http_max_size must be < %d but found %d\", int64(math.MaxInt64), v)\n\t}\n\n\tif a.GCSTimeout == nil {\n\t\treturn fmt.Errorf(\"gcs_timeout must be set\")\n\t}\n\tif v, err := time.ParseDuration(*a.GCSTimeout); err != nil {\n\t\treturn fmt.Errorf(\"gcs_timeout not a valid duration: %w\", err)","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/config/artifact.go#L152-L188","documentation":"After confirming http_read_timeout is present, Validate parses it with time.ParseDuration and wraps any parse failure with this error (using %w so the underlying ParseDuration error is preserved). It means the configured value is a string but not in Go duration syntax like \"30s\", \"5m\", or \"1h30m\".","triggerScenarios":"Setting artifact { http_read_timeout = 30 } (a number, coerced to a wrong string), \"30\", \"30 sec\", or any value lacking a unit suffix in the Nomad client config.","commonSituations":"Users writing bare numbers assuming seconds; copy-pasted values with units Go doesn't accept (e.g. \"ms\" typos like \"30sec\"); YAML/JSON configs where the value arrived as an int and was stringified as \"30\".","solutions":["Use Go duration syntax with a unit: http_read_timeout = \"30s\".","Never set a bare number; ensure the config layer emits a string, not a numeric type.","Validate the config with 'nomad agent -config ... ' in a CI dry-run before deploying.","Read the wrapped %w error text from ParseDuration for the exact offending position."],"exampleFix":"// before\nartifact {\n  http_read_timeout = 30\n}\n// after\nartifact {\n  http_read_timeout = \"30s\"\n}","handlingStrategy":"validation","validationCode":"if _, err := time.ParseDuration(cfg.Artifact.HTTPReadTimeout); err != nil {\n    return fmt.Errorf(\"http_read_timeout must be a Go duration like 30s: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := cfg.Artifact.Validate(); err != nil {\n    if strings.Contains(err.Error(), \"not a valid duration\") {\n        // fix the value to Go duration syntax, e.g. \"30s\"\n    }\n    return err\n}","preventionTips":["Always include a unit suffix: s, m, or h.","Quote duration values in HCL so they remain strings.","Lint config files with time.ParseDuration before shipping.","Avoid bare numbers and non-Go unit spellings like 'sec' or 'min'."],"tags":["nomad","config","validation","duration-parsing"],"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"}