hashicorp/nomad · error

hg_timeout must be > 0

Error message

hg_timeout must be > 0

What it means

Nomad's ArtifactConfig.Validate requires hg_timeout to be set and rejects negative durations. A negative value passed the nil and ParseDuration checks but failed the `v < 0` comparison, so the Mercurial artifact-download timeout is invalid. Only zero and positive durations are accepted (zero meaning no explicit timeout beyond defaults).

Source

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

		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)
	} 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)
	}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Change hg_timeout in the artifact block to a positive duration, e.g. hg_timeout = "30m".
  2. Remove the explicit hg_timeout so DefaultArtifactConfig() supplies a valid default.
  3. If hg_timeout is computed from a variable, clamp it with a max(0, v) or abs() before formatting the duration string.

Example fix

// before
artifact {
  hg_timeout = "-30s"
}
// after
artifact {
  hg_timeout = "30s"
}
Defensive patterns

Strategy: validation

Validate before calling

d, err := time.ParseDuration(cfg.HgTimeout)
if err != nil || d < 0 {
    return fmt.Errorf("hg_timeout must be a non-negative Go duration, got %q", cfg.HgTimeout)
}

Type guard

func validHgTimeout(a *ArtifactConfig) bool {
    if a.HgTimeout == nil { return false }
    d, err := time.ParseDuration(*a.HgTimeout)
    return err == nil && d >= 0
}

Prevention

When it happens

Trigger: Calling ArtifactConfig.Validate() when a.HgTimeout is a non-nil pointer to a duration string that time.ParseDuration accepts but is negative, e.g. *a.HgTimeout == "-30s" or "-5m".

Common situations: Hand-written client or agent hcl/json artifact blocks where a minus sign was typed accidentally; templated config that interpolates a negative computed offset; config copied from tooling that treats -1 as 'disable timeout' (Nomad does not).

Understand the failure class

Related errors


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