hashicorp/nomad · error
exposed service check
Error message
exposed service check %s[%s]->%s->%s is not a task-group service
What it means
Nomad only allows exposed (expose=true) service checks on task-group-level services backed by a Consul Connect sidecar. A check defined on a service inside a task's service block can never be exposed, because expose paths are wired into the group's Envoy proxy namespace. Validation fails fast with the group, task, service, and check names.
Solutions
- Move the service (and its exposed check) out of the task and up to the task group level, adding connect { sidecar_service {} } if needed.
- Remove expose = true from the task-service check and use a normal check with a port instead.
- Split the app so the exposed dependency is registered as a group service.
Example fix
// before
task "web" {
service {
name = "web"
check { expose = true }
}
}
// after
group "app" {
service {
name = "web"
connect { sidecar_service {} }
check { expose = true }
}
task "web" { ... }
} Defensive patterns
Strategy: validation
Validate before calling
// Ensure no task-level service declares an exposed check
job.TaskGroups.each { tg ->
tg.Tasks.each { t ->
(t.Services || []).each { s ->
(s.Checks || []).each { c ->
if c.Expose) throw new Error(`exposed check '${c.Name}' found in task '${t.Name}' service '${s.Name}'; move service to group level`)
}
}
}
} Type guard
func isGroupService(tg *api.TaskGroup, svc *api.Service) bool {
for _, s := range tg.Services {
if s.Name == svc.Name {
return true
}
}
return false
} Try / catch
// golang
if _, err := client.Jobs().Validate(job); err != nil {
if strings.Contains(err.Error(), "is not a task-group service") {
// relocate service block to group level or drop expose
}
} Prevention
- Put all Connect-enabled services at the task group level, not inside tasks.
- Search task stanzas for expose = true before submitting.
- Use nomad job validate in CI to catch placement mistakes.
- Document that expose applies only to group services in team job templates.
When it happens
Trigger: Submitting a job where any task-level service (inside task { service { ... } }) declares a check with expose = true, regardless of connect configuration.
Common situations: Developers moving expose-enabled checks from group services into task services during refactoring; mistaking task services for group services in HCL; templated jobs that hoist checks into task blocks.
Related errors
- exposed service check
- Consul Connect group
- Consul Connect service name
- ErrConnectInvalidNetworkMode
- ErrConnectRequireOneNetwork
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/62327730a197ef21.
Report an issue: GitHub.
Appendix: source
Thrown at nomad/job_endpoint_hook_expose_check.go:122
func tgValidateUseOfCheckExpose(tg *structs.TaskGroup) error {
// validation for group services (which must use built-in connect proxy)
for _, s := range tg.Services {
for _, check := range s.Checks {
if check.Expose && !s.Connect.HasSidecar() {
return fmt.Errorf(
"exposed service check %s->%s->%s requires use of sidecar_proxy",
tg.Name, s.Name, check.Name,
)
}
}
}
// validation for task services (which must not be configured to use Expose)
for _, t := range tg.Tasks {
for _, s := range t.Services {
for _, check := range s.Checks {
if check.Expose {
return fmt.Errorf(
"exposed service check %s[%s]->%s->%s is not a task-group service",
tg.Name, t.Name, s.Name, check.Name,
)
}
}
}
}
return nil
}
// tgValidateExposeNetworkMode ensures there is exactly 1 network configured for
// the task group, and that it uses "bridge" or "cni/*" mode (i.e. enables network
// namespaces).
func tgValidateExposeNetworkMode(tg *structs.TaskGroup) error {
if tgUsesExposeCheck(tg) {
return groupConnectNetworkModeValidate(tg, "connect expose check", false)
}
return nilView on GitHub (pinned to 482b49bf1a)