{"record":{"id":"c1c98e0bc5dd8f1e","repo":"hashicorp/nomad","slug":"must-specify-a-destination-for-the-template","errorCode":null,"errorMessage":"Must specify a destination for the template","messagePattern":"Must specify a destination for the template","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/structs.go","lineNumber":8969,"sourceCode":"}\n\nfunc (t *Template) Canonicalize() {\n\tif t.ChangeSignal != \"\" {\n\t\tt.ChangeSignal = strings.ToUpper(t.ChangeSignal)\n\t}\n}\n\nfunc (t *Template) Validate() error {\n\tvar mErr multierror.Error\n\n\t// Verify we have something to render\n\tif t.SourcePath == \"\" && t.EmbeddedTmpl == \"\" {\n\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Must specify a source path or have an embedded template\"))\n\t}\n\n\t// Verify we can render somewhere\n\tif t.DestPath == \"\" {\n\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Must specify a destination for the template\"))\n\t}\n\n\t// Verify the destination doesn't escape\n\tescaped, err := escapingfs.PathEscapesAllocViaRelative(\"task\", t.DestPath)\n\tif err != nil {\n\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"invalid destination path: %v\", err))\n\t} else if escaped {\n\t\tmErr.Errors = append(mErr.Errors, fmt.Errorf(\"destination escapes allocation directory\"))\n\t}\n\n\t// Verify a proper change mode\n\tswitch t.ChangeMode {\n\tcase TemplateChangeModeNoop, TemplateChangeModeRestart:\n\tcase TemplateChangeModeSignal:\n\t\tif t.ChangeSignal == \"\" {\n\t\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Must specify signal value when change mode is signal\"))\n\t\t}\n\t\tif t.Envvars {","sourceCodeStart":8951,"sourceCodeEnd":8987,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/structs.go#L8951-L8987","documentation":"Template.Validate() requires a destination path where the rendered template will be written inside the allocation directory. When DestPath is empty, validation appends this error. Nomad cannot render a template without knowing where to place its output.","triggerScenarios":"Submitting a job with template { source = \"x.tpl\" } (or data) but no `destination` field. Triggered when t.DestPath == \"\" in Template.Validate().","commonSituations":"Forgot the destination line; templating tooling omitted it; assuming Nomad writes next to the source by default (it does not — destination is mandatory).","solutions":["Add `destination = \"local/<file>\"` to the template stanza (typically under local/ in the alloc dir).","Re-run `nomad job validate` to confirm the template now passes.","Check that job-generation tooling always sets destination."],"exampleFix":"// before\ntemplate {\n  data = \"key: value\"\n}\n// after\ntemplate {\n  data        = \"key: value\"\n  destination = \"local/config.yml\"\n}","handlingStrategy":"validation","validationCode":"// Pre-submit check\nif tpl.Destination == \"\" {\n  return errors.New(\"template requires a destination, e.g. local/app.conf\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Adopt the convention destination = \"local/<filename>\" for every template.","Never rely on a default destination — Nomad has none.","Lint job HCL for template stanzas missing destination."],"tags":["nomad","template","job-spec-validation"],"backgroundTag":"missing-template-destination","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}