hashicorp/nomad · error

Connect proxy task must not have a service block

Error message

Connect proxy task must not have a service block

What it means

For Consul Connect proxy tasks (task kind is connect-proxy), Task.Validate forbids declaring `service` blocks on the task itself, emitting "Connect proxy task must not have a service block". Proxy services are defined at group level and wired automatically by Connect, so per-task services are invalid.

Source

Thrown at nomad/structs/structs.go:8382

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

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

	}

	// Validation for TaskKind field which is used for Consul Connect integration
	if t.Kind.IsConnectProxy() {
		// This task is a Connect proxy so it should not have service blocks
		if len(t.Services) > 0 {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Connect proxy task must not have a service block"))
		}
		if t.Leader {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Connect proxy task must not have leader set"))
		}

		// Ensure the proxy task has a corresponding service entry
		serviceErr := ValidateConnectProxyService(t.Kind.Value(), tg.Services)
		if serviceErr != nil {
			mErr.Errors = append(mErr.Errors, serviceErr)
		}
	}

	// Validation for volumes
	for idx, vm := range t.VolumeMounts {
		if _, ok := tg.Volumes[vm.Volume]; !ok {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Volume Mount (%d) references undefined volume %s", idx, vm.Volume))
		}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Remove the service blocks from the connect-proxy task
  2. Declare the proxy service via the group's `connect { sidecar_service { proxy {} } }` stanza instead
  3. Let Nomad generate the proxy task rather than writing kind="connect-proxy" manually

Example fix

// before
task "mesh-proxy" {
  kind = "connect-proxy:api"
  service {
    name = "api"
  }
}
// after
group "api" {
  connect {
    sidecar_service {
      proxy {}
    }
  }
}
Defensive patterns

Strategy: validation

Validate before calling

if task.Kind.IsConnectProxy() && len(task.Services) > 0 {
    return fmt.Errorf("connect proxy task %q must not declare services", task.Name)
}

Type guard

func isConnectProxy(t *structs.Task) bool { return t.Kind.IsConnectProxy() }

Try / catch

if err := job.Validate(); err != nil {
    if strings.Contains(err.Error(), "must not have a service block") { /* move services to group/connect */ }
}

Prevention

When it happens

Trigger: Defining a task with `kind = "connect-proxy:..."` (or generated proxy tasks) while also listing `service` blocks inside that task.

Common situations: Hand-crafting connect proxy tasks instead of using `connect { sidecar_service {} }`; copying a normal task spec and adding the connect-proxy kind; generated jobs from older tooling.

Related errors


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