hashicorp/nomad · error

gcs_timeout not a valid duration: %w

Error message

gcs_timeout not a valid duration: %w

What it means

Returned by ArtifactConfig.Validate when gcs_timeout is set but cannot be parsed by time.ParseDuration as a Go duration string. Valid formats include "300ms", "5m", "1h30m"; the wrapped error pinpoints the invalid syntax.

Source

Thrown at nomad/structs/config/artifact.go:188

		return fmt.Errorf("http_read_timeout not a valid duration: %w", err)
	} else if v < 0 {
		return fmt.Errorf("http_read_timeout must be > 0")
	}

	if a.HTTPMaxSize == nil {
		return fmt.Errorf("http_max_size must be set")
	}
	if v, err := humanize.ParseBytes(*a.HTTPMaxSize); err != nil {
		return fmt.Errorf("http_max_size not a valid size: %w", err)
	} else if v > math.MaxInt64 {
		return fmt.Errorf("http_max_size must be < %d but found %d", int64(math.MaxInt64), v)
	}

	if a.GCSTimeout == nil {
		return fmt.Errorf("gcs_timeout must be set")
	}
	if v, err := time.ParseDuration(*a.GCSTimeout); err != nil {
		return fmt.Errorf("gcs_timeout not a valid duration: %w", err)
	} else if v < 0 {
		return fmt.Errorf("gcs_timeout must be > 0")
	}

	if a.GitTimeout == nil {
		return fmt.Errorf("git_timeout must be set")
	}
	if v, err := time.ParseDuration(*a.GitTimeout); err != nil {
		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)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Use a valid Go duration string with a unit suffix, e.g. "5m", "300s", "1h".
  2. Ensure templated/env-substituted values include the unit (e.g. append "s" for seconds).
  3. Check the wrapped %w message to find the exact invalid portion of the string.
  4. Validate the config with `nomad validate` before restarting the agent.

Example fix

// before
artifact {
  gcs_timeout = "300"
}
// after
artifact {
  gcs_timeout = "300s"
}
Defensive patterns

Strategy: validation

Validate before calling

func validDuration(s *string) bool {
    if s == nil { return false }
    _, err := time.ParseDuration(*s)
    return err == nil
}

Type guard

func isDuration(s string) bool {
    _, err := time.ParseDuration(s)
    return err == nil
}

Prevention

When it happens

Trigger: Setting `gcs_timeout` to values like "5" (no unit), "5 minutes" (space and full word), "5m30" (trailing partial), "" (empty), or numeric-only input from environment/templating substitution.

Common situations: Confusing Go duration syntax with seconds-integer conventions used elsewhere; templating injecting an unquoted number; typos such as "300sec" or "5min"; copying values from non-Nomad tools with different duration formats.

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


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/b0215417be28cee6. Report an issue: GitHub.