hashicorp/nomad · error

service[%d] %+q validation failed: %s

Error message

service[%d] %+q validation failed: %s

What it means

In Task.Validate, each entry of t.Services is validated by Service.Validate; failures are collected as 'service[%d] %+q validation failed: %s' with the service index, name, and wrapped validation errors. It aggregates any problem with a service definition: bad name characters, missing checks fields, invalid address_mode, invalid provider, etc.

Source

Thrown at nomad/structs/structs.go:8481

// validateServices takes a task and validates the services within it are valid
// and reference ports that exist.
func validateServices(t *Task, tgNetworks Networks) error {
	var mErr multierror.Error

	// Ensure that services don't ask for nonexistent ports and their names are
	// unique.
	servicePorts := make(map[string]map[string]struct{})
	addServicePort := func(label, service string) {
		if _, ok := servicePorts[label]; !ok {
			servicePorts[label] = map[string]struct{}{}
		}
		servicePorts[label][service] = struct{}{}
	}
	knownServices := make(map[string]struct{})
	for i, service := range t.Services {
		if err := service.Validate(); err != nil {
			outer := fmt.Errorf("service[%d] %+q validation failed: %s", i, service.Name, err)
			mErr.Errors = append(mErr.Errors, outer)
		}

		if service.AddressMode == AddressModeAlloc {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("service %q cannot use address_mode=\"alloc\", only services defined in a \"group\" block can use this mode", service.Name))
		}

		if service.AddressMode == AddressModeAllocIPv6 {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("service %q cannot use address_mode=\"alloc_ipv6\", only services defined in a \"group\" block can use this mode", service.Name))
		}

		// Ensure that services with the same name are not being registered for
		// the same port
		if _, ok := knownServices[service.Name+service.PortLabel]; ok {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("service %q is duplicate", service.Name))
		}
		knownServices[service.Name+service.PortLabel] = struct{}{}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Read the wrapped %s error after 'validation failed:' for the concrete field problem
  2. Fix the service name to use valid characters and declare the referenced port in resources
  3. Correct provider/check/address_mode fields per docs and run `nomad job validate` before submitting

Example fix

// before
service {
  name = "my/app"
  port = "http"
}
// after
service {
  name = "my-app"
  port = "http"
}
Defensive patterns

Strategy: validation

Validate before calling

for i, svc := range t.Services {
    if err := svc.Validate(); err != nil {
        return fmt.Errorf("service[%d] %q bad: %w", i, svc.Name, err)
    }
}

Try / catch

if err := job.Validate(); err != nil {
    // match 'service[N] "name" validation failed:' and report the inner cause
}

Prevention

When it happens

Trigger: A task service block failing Service.Validate — e.g. name with invalid characters for Consul/Nomad DNS, check with both address_mode issues, port label not defined, unsupported provider value, or invalid upstream config in connect blocks.

Common situations: Service names containing slashes or characters Consul rejects; port labels referencing ports not declared in the task's resources; typos in provider ("consul" vs "nomad"); service blocks copied between group and task level where address_mode="alloc" is invalid (that case also yields the follow-up alloc-mode error).

Related errors


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