{"record":{"id":"df06dfc2b50c21fd","repo":"hashicorp/nomad","slug":"s3-timeout-not-a-valid-duration-w","errorCode":null,"errorMessage":"s3_timeout not a valid duration: %w","messagePattern":"s3_timeout not a valid duration: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/config/artifact.go","lineNumber":215,"sourceCode":"\t\treturn fmt.Errorf(\"git_timeout not a valid duration: %w\", err)\n\t} else if v < 0 {\n\t\treturn fmt.Errorf(\"git_timeout must be > 0\")\n\t}\n\n\tif a.HgTimeout == nil {\n\t\treturn fmt.Errorf(\"hg_timeout must be set\")\n\t}\n\tif v, err := time.ParseDuration(*a.HgTimeout); err != nil {\n\t\treturn fmt.Errorf(\"hg_timeout not a valid duration: %w\", err)\n\t} else if v < 0 {\n\t\treturn fmt.Errorf(\"hg_timeout must be > 0\")\n\t}\n\n\tif a.S3Timeout == nil {\n\t\treturn fmt.Errorf(\"s3_timeout must be set\")\n\t}\n\tif v, err := time.ParseDuration(*a.S3Timeout); err != nil {\n\t\treturn fmt.Errorf(\"s3_timeout not a valid duration: %w\", err)\n\t} else if v < 0 {\n\t\treturn fmt.Errorf(\"s3_timeout must be > 0\")\n\t}\n\n\tif a.DecompressionFileCountLimit == nil {\n\t\treturn fmt.Errorf(\"decompression_file_count_limit must not be nil\")\n\t}\n\tif v := *a.DecompressionFileCountLimit; v < 0 {\n\t\treturn fmt.Errorf(\"decompression_file_count_limit must be >= 0 but found %d\", v)\n\t}\n\n\tif a.DecompressionSizeLimit == nil {\n\t\treturn fmt.Errorf(\"decompression_size_limit must not be nil\")\n\t}\n\tif v, err := humanize.ParseBytes(*a.DecompressionSizeLimit); err != nil {\n\t\treturn fmt.Errorf(\"decompression_size_limit is not a valid size: %w\", err)\n\t} else if v > math.MaxInt64 {\n\t\treturn fmt.Errorf(\"decompression_size_limit must be < %d but found %d\", int64(math.MaxInt64), v)","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/config/artifact.go#L197-L233","documentation":"After confirming s3_timeout is set, Validate() parses it with time.ParseDuration. If the string is not a Go duration literal, the parse error is wrapped as 's3_timeout not a valid duration: %w'. Go requires a number plus unit (e.g. 30s, 1h30m); bare integers like \"300\" fail.","triggerScenarios":"Calling ArtifactConfig.Validate() when *a.S3Timeout cannot be parsed by time.ParseDuration, e.g. \"300\" (no unit), \"thirty minutes\", or \"1H \" with stray characters.","commonSituations":"Users writing s3_timeout = 300 instead of \"300s\" in HCL; copying Unix-style second counts from other tools; locale-formatted strings with commas or spaces; YAML/JSON quoting problems that mangle the value.","solutions":["Rewrite s3_timeout with a unit suffix, e.g. s3_timeout = \"5m\" instead of \"300\".","Validate locally with go tool: time.ParseDuration(value) must return nil error before deploying.","If the value comes from another system that emits seconds, convert programmatically: fmt.Sprintf(\"%ds\", seconds)."],"exampleFix":"// before\nartifact {\n  s3_timeout = \"300\"\n}\n// after\nartifact {\n  s3_timeout = \"5m\"\n}","handlingStrategy":"validation","validationCode":"if _, err := time.ParseDuration(cfg.S3Timeout); err != nil {\n    return fmt.Errorf(\"s3_timeout %q must look like '5m' or '300s': %w\", cfg.S3Timeout, err)\n}","typeGuard":"func validS3Timeout(s *string) bool {\n    if s == nil { return false }\n    _, err := time.ParseDuration(*s)\n    return err == nil\n}","tryCatchPattern":null,"preventionTips":["Always include a time unit: s, m, h (e.g. \"300s\", not \"300\").","Add a config lint step that calls time.ParseDuration on every *_timeout field.","Quote duration values in HCL/JSON to avoid type coercion surprises."],"tags":["nomad","config-validation","duration-parse","s3"],"backgroundTag":"invalid-duration-config","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"}