{"record":{"id":"634a5b5a3affd713","repo":"hashicorp/nomad","slug":"must-specify-a-spec","errorCode":null,"errorMessage":"Must specify a spec","messagePattern":"Must specify a spec","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/structs.go","lineNumber":5921,"sourceCode":"\tif p == nil {\n\t\treturn nil\n\t}\n\tnp := new(PeriodicConfig)\n\t*np = *p\n\treturn np\n}\n\nfunc (p *PeriodicConfig) Validate() error {\n\tif !p.Enabled {\n\t\treturn nil\n\t}\n\n\tvar mErr multierror.Error\n\tif p.Spec != \"\" && len(p.Specs) != 0 {\n\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Only cron or crons may be used\"))\n\t}\n\tif p.Spec == \"\" && len(p.Specs) == 0 {\n\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Must specify a spec\"))\n\t}\n\n\t// Check if we got a valid time zone\n\tif p.TimeZone != \"\" {\n\t\tif _, err := time.LoadLocation(p.TimeZone); err != nil {\n\t\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Invalid time zone %q: %v\", p.TimeZone, err))\n\t\t}\n\t}\n\n\tswitch p.SpecType {\n\tcase PeriodicSpecCron:\n\t\t// Validate the cron spec\n\t\tif p.Spec != \"\" {\n\t\t\tif _, err := cronexpr.Parse(p.Spec); err != nil {\n\t\t\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Invalid cron spec %q: %v\", p.Spec, err))\n\t\t\t}\n\t\t}\n\t\t// Validate the cron specs","sourceCodeStart":5903,"sourceCodeEnd":5939,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/structs.go#L5903-L5939","documentation":"Returned by PeriodicConfig.Validate() when a periodic job defines neither a single cron spec (p.Spec) nor a multi-spec list (p.Specs) — there is no schedule to run. Appended to a multierror and surfaced on job submission/validation.","triggerScenarios":"Submitting a job with `type = \"periodic\"` and a `periodic {}` block that is empty or whose cron/crons fields are blank strings/empty lists; programmatic creation of PeriodicConfig without calling SetSpec/SetSpecs.","commonSituations":"Template rendering that dropped the cron line; hand-written HCL missing the cron field; SDK/API usage constructing PeriodicConfig{} without a spec; YAML/JSON conversion losing empty fields.","solutions":["Add a `cron = \"...\"` (or `crons = [...]`) entry to the periodic block","Verify your templating/HCL rendering actually emits the cron field","In code, call PeriodicConfig.SetSpec(...) before Validate","Run `nomad job validate my.nomad.hcl` to catch it before submission"],"exampleFix":"// before\nperiodic {\n  prohibit_overlap = true\n}\n// after\nperiodic {\n  cron = \"*/5 * * * * *\"\n  prohibit_overlap = true\n}","handlingStrategy":"validation","validationCode":"if pc.Spec == \"\" && len(pc.Specs) == 0 {\n    return fmt.Errorf(\"periodic job requires a cron or crons schedule\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair type=\"periodic\" with a non-empty cron/crons field","Check template rendering output for the cron line","Call PeriodicConfig.SetSpec in programmatic job builders","Run nomad job validate in CI for all job files"],"tags":["nomad","periodic-job","validation"],"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"}