hashicorp/nomad · error

Template %d validation failed: %s

Error message

Template %d validation failed: %s

What it means

Nomad's Task.Validate wraps each template block validation failure in "Template %d validation failed: %s" (1-based index) and appends it to the job's multi-error. The underlying message comes from Template.Validate and describes what is wrong (e.g. missing dest_path, bad splay, invalid source). It is returned by job validation endpoints and surfaces in CLI output such as `nomad job run` or `nomad job allocs` error text.

Source

Thrown at nomad/structs/structs.go:8335

	for idx, artifact := range t.Artifacts {
		if err := artifact.Validate(); err != nil {
			outer := fmt.Errorf("Artifact %d validation failed: %v", idx+1, err)
			mErr.Errors = append(mErr.Errors, outer)
		}
	}

	// Validate Vault.
	if t.Vault != nil {
		if err := t.Vault.Validate(); err != nil {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Vault validation failed: %v", err))
		}
	}

	// Validate templates.
	destinations := make(map[string]int, len(t.Templates))
	for idx, tmpl := range t.Templates {
		if err := tmpl.Validate(); err != nil {
			outer := fmt.Errorf("Template %d validation failed: %s", idx+1, err)
			mErr.Errors = append(mErr.Errors, outer)
		}

		if other, ok := destinations[tmpl.DestPath]; ok {
			outer := fmt.Errorf("Template %d has same destination as %d", idx+1, other)
			mErr.Errors = append(mErr.Errors, outer)
		} else {
			destinations[tmpl.DestPath] = idx + 1
		}
	}

	// Validate actions.
	actions := make(map[string]bool)
	for _, action := range t.Actions {
		if err := action.Validate(); err != nil {
			outer := fmt.Errorf("Action %s validation failed: %s", action.Name, err)
			mErr.Errors = append(mErr.Errors, outer)
		}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Read the wrapped inner error (after 'validation failed: ') to see which template field is wrong
  2. Fix the offending field in the Nth template block (index is 1-based per task)
  3. Run `nomad job validate <jobfile>` before submitting to catch all template errors at once
  4. Check Nomad version docs for template options supported in your deployment

Example fix

// before
template {
  data = "{{ env \"attr.unique.network.ip-address\" }}"
}
// after (missing dest_path added)
template {
  data      = "{{ env \"attr.unique.network.ip-address\" }}"
  destination = "local/ip.txt"
}
Defensive patterns

Strategy: validation

Validate before calling

for i, tmpl := range task.Templates {
    if err := tmpl.Validate(); err != nil {
        return fmt.Errorf("template %d: %w", i+1, err)
    }
}

Type guard

func hasTemplates(t *structs.Task) bool { return len(t.Templates) > 0 }

Try / catch

if err := job.Validate(); err != nil {
    var mErr structs.MultiError
    if errors.As(err, &mErr) {
        for _, e := range mErr.Errors {
            if strings.Contains(e.Error(), "Template ") { /* handle */ }
        }
    }
}

Prevention

When it happens

Trigger: Submitting a job whose task stanza contains a template block that fails Template.Validate: missing destination, template source referencing missing artifact, invalid splay duration, embedded template parse errors, or duplicate/invalid env targets.

Common situations: Typo in template stanza fields; using consul-template functions unsupported by the Nomad version; dest_path pointing outside the task dir with wrong permissions; misconfigured change_mode/change_script options.

Related errors


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