{"record":{"id":"34176273a926657d","repo":"hashicorp/nomad","slug":"s3-timeout-must-be-set","errorCode":null,"errorMessage":"s3_timeout must be set","messagePattern":"s3_timeout must be set","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/config/artifact.go","lineNumber":212,"sourceCode":"\t\treturn fmt.Errorf(\"git_timeout must be set\")\n\t}\n\tif v, err := time.ParseDuration(*a.GitTimeout); err != nil {\n\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 {","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/config/artifact.go#L194-L230","documentation":"ArtifactConfig.Validate() checks every artifact option for nil so that fully-populated configs are explicit. When a.S3Timeout is nil, meaning the s3_timeout field was never assigned, validation fails immediately with 's3_timeout must be set'. It is a completeness check, not a value check.","triggerScenarios":"Calling ArtifactConfig.Validate() on an ArtifactConfig struct (or parsed config) where the s3_timeout key is absent, so a.S3Timeout remains a nil *string.","commonSituations":"Constructing ArtifactConfig manually in Go tests or plugins without using DefaultArtifactConfig(); older config files written before s3_timeout was introduced being loaded by a newer Nomad; partial config merges that drop the key instead of inheriting defaults.","solutions":["Add s3_timeout = \"30m\" (or another positive duration) to the artifact block.","Initialize the struct with DefaultArtifactConfig() before overriding fields, so all pointer fields are non-nil.","If merging configs, ensure defaults are applied last so absent keys inherit from DefaultArtifactConfig()."],"exampleFix":"// before\nfunc newArtifactConfig() *ArtifactConfig {\n  return &ArtifactConfig{} // S3Timeout nil\n}\n// after\nfunc newArtifactConfig() *ArtifactConfig {\n  c := DefaultArtifactConfig()\n  c.S3Timeout = stringPtr(\"1h\")\n  return c\n}","handlingStrategy":"validation","validationCode":"if cfg.S3Timeout == nil {\n    return errors.New(\"artifact config must set s3_timeout; start from DefaultArtifactConfig()\")\n}","typeGuard":"func hasS3Timeout(a *ArtifactConfig) bool { return a.S3Timeout != nil && *a.S3Timeout != \"\" }","tryCatchPattern":null,"preventionTips":["Always build ArtifactConfig starting from DefaultArtifactConfig().","Keep config files updated when upgrading Nomad versions (new required keys).","Validate merged config after every merge step, not just at startup."],"tags":["nomad","config-validation","missing-field","s3"],"backgroundTag":"missing-required-argument","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"}