hashicorp/nomad · error
service %q cannot have "connect" block, only services define
Error message
service %q cannot have "connect" block, only services defined in a "group" block can
What it means
Connect (Consul service mesh) integration is only supported for services declared at the group level, because Connect sidecar proxies and networking are managed per allocation, not per task. A task-level service carrying a `connect` block is rejected during job validation.
Source
Thrown at nomad/structs/structs.go:8515
}
knownServices[service.Name+service.PortLabel] = struct{}{}
if service.PortLabel != "" {
if service.AddressMode == "driver" {
// Numeric port labels are valid for address_mode=driver
_, err := strconv.Atoi(service.PortLabel)
if err != nil {
// Not a numeric port label, add it to list to check
addServicePort(service.PortLabel, service.Name)
}
} else {
addServicePort(service.PortLabel, service.Name)
}
}
// connect block is only allowed on group level
if service.Connect != nil {
mErr.Errors = append(mErr.Errors, fmt.Errorf("service %q cannot have \"connect\" block, only services defined in a \"group\" block can", service.Name))
}
// Ensure that check names are unique and have valid ports
knownChecks := make(map[string]struct{})
for _, check := range service.Checks {
if _, ok := knownChecks[check.Name]; ok {
mErr.Errors = append(mErr.Errors, fmt.Errorf("check %q is duplicate", check.Name))
}
knownChecks[check.Name] = struct{}{}
if check.AddressMode == AddressModeAlloc {
mErr.Errors = append(mErr.Errors, fmt.Errorf("check %q cannot use address_mode=\"alloc\", only checks defined in a \"group\" service block can use this mode", service.Name))
}
if check.AddressMode == AddressModeAllocIPv6 {
mErr.Errors = append(mErr.Errors, fmt.Errorf("check %q cannot use address_mode=\"alloc_ipv6\", only checks defined in a \"group\" service block can use this mode", service.Name))
}
View on GitHub (pinned to 482b49bf1a)
Solutions
- Move the service (with its connect block) up to the `group` level.
- Remove the connect block if mesh is not needed for the task-level service.
- Re-run `nomad job validate` after restructuring to confirm compliance.
Example fix
// before
task "api" {
service {
name = "api"
connect { sidecar_service {} }
}
}
// after
group "api" {
network { mode = "cni/bridge" }
service {
name = "api"
connect { sidecar_service {} }
}
task "api" { }
} Defensive patterns
Strategy: validation
Validate before calling
for _, tg := range job.TaskGroups {
for _, t := range tg.Tasks {
for _, s := range t.Services {
if s.Connect != nil {
return fmt.Errorf("service %q in task %s: connect block must be at group level", s.Name, t.Name)
}
}
}
} Prevention
- Always declare Connect-enabled services in the group block
- Treat task-level services as driver-addressed only
- Follow upstream Nomad Connect examples which place service at group level
When it happens
Trigger: Submitting a job where a service nested inside a `task` block contains a `connect { sidecar_service {} }` (or any connect) stanza.
Common situations: Following Consul-connect examples that place service at group level but implementing them inside a task; migrating a task-scoped service to service mesh; copy-paste from group-level examples.
Related errors
- No Connect services in task group with Connect proxy (%q)
- Connect proxy service name (%q) not found in Connect service
- envoy must be used as connect sidecar or gateway
- ErrConnectRequireOneNetwork
- ErrConnectInvalidNetworkMode
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/f62dfdf7857abc19.
Report an issue: GitHub.