hashicorp/nomad · error
s3_timeout not a valid duration: %w
Error message
s3_timeout not a valid duration: %w
What it means
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.
Source
Thrown at nomad/structs/config/artifact.go:215
return fmt.Errorf("git_timeout not a valid duration: %w", err)
} else if v < 0 {
return fmt.Errorf("git_timeout must be > 0")
}
if a.HgTimeout == nil {
return fmt.Errorf("hg_timeout must be set")
}
if v, err := time.ParseDuration(*a.HgTimeout); err != nil {
return fmt.Errorf("hg_timeout not a valid duration: %w", err)
} else if v < 0 {
return fmt.Errorf("hg_timeout must be > 0")
}
if a.S3Timeout == nil {
return fmt.Errorf("s3_timeout must be set")
}
if v, err := time.ParseDuration(*a.S3Timeout); err != nil {
return fmt.Errorf("s3_timeout not a valid duration: %w", err)
} else if v < 0 {
return fmt.Errorf("s3_timeout must be > 0")
}
if a.DecompressionFileCountLimit == nil {
return fmt.Errorf("decompression_file_count_limit must not be nil")
}
if v := *a.DecompressionFileCountLimit; v < 0 {
return fmt.Errorf("decompression_file_count_limit must be >= 0 but found %d", v)
}
if a.DecompressionSizeLimit == nil {
return fmt.Errorf("decompression_size_limit must not be nil")
}
if v, err := humanize.ParseBytes(*a.DecompressionSizeLimit); err != nil {
return fmt.Errorf("decompression_size_limit is not a valid size: %w", err)
} else if v > math.MaxInt64 {
return fmt.Errorf("decompression_size_limit must be < %d but found %d", int64(math.MaxInt64), v)View on GitHub (pinned to 482b49bf1a)
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).
Example fix
// before
artifact {
s3_timeout = "300"
}
// after
artifact {
s3_timeout = "5m"
} Defensive patterns
Strategy: validation
Validate before calling
if _, err := time.ParseDuration(cfg.S3Timeout); err != nil {
return fmt.Errorf("s3_timeout %q must look like '5m' or '300s': %w", cfg.S3Timeout, err)
} Type guard
func validS3Timeout(s *string) bool {
if s == nil { return false }
_, err := time.ParseDuration(*s)
return err == nil
} Prevention
- 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.
When it happens
Trigger: Calling ArtifactConfig.Validate() when *a.S3Timeout cannot be parsed by time.ParseDuration, e.g. "300" (no unit), "thirty minutes", or "1H " with stray characters.
Common situations: 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.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- s3_timeout must be set
- s3_timeout must be > 0
- failed to parse config:
- only one of cgroups_v1_override and cgroups_v2_override may
- work_dir must be an absolute path
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/df06dfc2b50c21fd.
Report an issue: GitHub.