{"record":{"id":"014c341dd9767c8f","repo":"hashicorp/nomad","slug":"error-parsing-gcstimeout-w","errorCode":null,"errorMessage":"error parsing GCSTimeout: %w","messagePattern":"error parsing GCSTimeout: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/config/artifact.go","lineNumber":50,"sourceCode":"\tSetEnvironmentVariables       string\n}\n\n// ArtifactConfigFromAgent creates a new internal readonly copy of the client\n// agent's ArtifactConfig. The config should have already been validated.\nfunc ArtifactConfigFromAgent(c *config.ArtifactConfig) (*ArtifactConfig, error) {\n\thttpReadTimeout, err := time.ParseDuration(*c.HTTPReadTimeout)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error parsing HTTPReadTimeout: %w\", err)\n\t}\n\n\thttpMaxSize, err := humanize.ParseBytes(*c.HTTPMaxSize)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error parsing HTTPMaxSize: %w\", err)\n\t}\n\n\tgcsTimeout, err := time.ParseDuration(*c.GCSTimeout)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error parsing GCSTimeout: %w\", err)\n\t}\n\n\tgitTimeout, err := time.ParseDuration(*c.GitTimeout)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error parsing GitTimeout: %w\", err)\n\t}\n\n\thgTimeout, err := time.ParseDuration(*c.HgTimeout)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error parsing HgTimeout: %w\", err)\n\t}\n\n\ts3Timeout, err := time.ParseDuration(*c.S3Timeout)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error parsing S3Timeout: %w\", err)\n\t}\n\n\tdecompressionSizeLimit, err := humanize.ParseBytes(*c.DecompressionSizeLimit)","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/config/artifact.go#L32-L68","documentation":"ArtifactConfigFromAgent parses GCSTimeout with time.ParseDuration to produce a time.Duration for GCS artifact downloads. A non-duration string cannot be converted, so the wrapped parse error is returned with this message and the ArtifactConfig is not built.","triggerScenarios":"Calling ArtifactConfigFromAgent when the agent ArtifactConfig's GCSTimeout points to a string like \"30\", \"30sec\", \"thirty seconds\", \"\" or \"1h30m\" misspellings that time.ParseDuration rejects.","commonSituations":"gcs_timeout set in the Nomad client artifact config block without a time unit (e.g. \"30\" instead of \"30s\"); values sourced from environment variables or templates that render empty; hand-written configs in tests.","solutions":["Append a valid Go duration unit to the value: use \"30s\", \"5m\", \"1h30m\" instead of a bare number","Check the rendered Nomad agent config for the gcs_timeout value and ensure it is non-empty and unit-suffixed","Pre-validate with time.ParseDuration in the code that assembles the config to fail earlier with context"],"exampleFix":"// before\ngcs_timeout = \"120\"\n// after\ngcs_timeout = \"120s\"","handlingStrategy":"validation","validationCode":"if _, err := time.ParseDuration(*c.GCSTimeout); err != nil {\n    return fmt.Errorf(\"invalid gcs_timeout %q: %w\", *c.GCSTimeout, err)\n}","typeGuard":"func validDuration(s *string) bool { if s == nil { return false }; _, err := time.ParseDuration(*s); return err == nil }","tryCatchPattern":"cfg, err := config.ArtifactConfigFromAgent(agentCfg)\nif err != nil {\n    if strings.Contains(err.Error(), \"error parsing GCSTimeout\") {\n        return fmt.Errorf(\"gcs_timeout must be a Go duration like 30s: %w\", err)\n    }\n    return err\n}","preventionTips":["Always include a unit in duration strings: \"30s\", \"5m\", \"1h\" — never a bare number","Run nomad agent config validation before deploying","Check templated configs for empty rendered values"],"tags":["config","parsing","duration","go"],"backgroundTag":"invalid-duration-or-size-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"}