{"record":{"id":"35783f2a3f8d5163","repo":"hashicorp/nomad","slug":"exposed-service-check-s-s-s-requires-use-of-s","errorCode":null,"errorMessage":"exposed service check %s->%s->%s requires use of sidecar_proxy","messagePattern":"exposed service check (.+?)->(.+?)->(.+?) requires use of sidecar_proxy","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/job_endpoint_hook_expose_check.go","lineNumber":109,"sourceCode":"\t\ts.Connect.SidecarService.Proxy.Expose = new(structs.ConsulExposeConfig)\n\t}\n\treturn s.Connect.SidecarService.Proxy.Expose\n}\n\n// containsExposePath returns true if path is contained in paths.\nfunc containsExposePath(paths []structs.ConsulExposePath, path structs.ConsulExposePath) bool {\n\treturn slices.Contains(paths, path)\n}\n\n// tgValidateUseOfCheckExpose ensures that any service check in tg making use\n// of the expose field is within an appropriate context to do so. The check must\n// be a group level check, and must use the builtin envoy proxy.\nfunc tgValidateUseOfCheckExpose(tg *structs.TaskGroup) error {\n\t// validation for group services (which must use built-in connect proxy)\n\tfor _, s := range tg.Services {\n\t\tfor _, check := range s.Checks {\n\t\t\tif check.Expose && !s.Connect.HasSidecar() {\n\t\t\t\treturn fmt.Errorf(\n\t\t\t\t\t\"exposed service check %s->%s->%s requires use of sidecar_proxy\",\n\t\t\t\t\ttg.Name, s.Name, check.Name,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\t// validation for task services (which must not be configured to use Expose)\n\tfor _, t := range tg.Tasks {\n\t\tfor _, s := range t.Services {\n\t\t\tfor _, check := range s.Checks {\n\t\t\t\tif check.Expose {\n\t\t\t\t\treturn fmt.Errorf(\n\t\t\t\t\t\t\"exposed service check %s[%s]->%s->%s is not a task-group service\",\n\t\t\t\t\t\ttg.Name, t.Name, s.Name, check.Name,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/job_endpoint_hook_expose_check.go#L91-L127","documentation":"Nomad's job validation rejects a group service check with expose=true when the service's Connect block has no sidecar_proxy. Exposed checks only work by routing through the Envoy sidecar that Consul Connect injects; without connect { sidecar_service {} } there is no proxy to bind the exposed listener in, so the job is invalid. HasSidecar() is simply `c != nil && c.SidecarService != nil` (nomad/structs/services.go:1286).","triggerScenarios":"Submitting (nomad job run / jobs API Validate) a job where a task-group-level service has a check with expose=true but the service's connect block is absent, or is connect { sidecar_service { ... } } missing, or uses connect native.","commonSituations":"Copy-pasting an expose example onto a service that only has connect {} without sidecar_service; assuming expose works with connect-native services; upgrading an HCL file where the sidecar block was removed but checks kept expose = true.","solutions":["Add connect { sidecar_service {} } to the group service so Envoy is injected.","Remove expose = true from the check if you don't need it exposed through the proxy.","Move the service to group level if it is currently a task service (task services can never use expose; see error 2346)."],"exampleFix":"// before\nservice {\n  name = \"api\"\n  connect {}\n  check {\n    expose = true\n  }\n}\n// after\nservice {\n  name = \"api\"\n  connect { sidecar_service {} }\n  check {\n    expose = true\n  }\n}","handlingStrategy":"validation","validationCode":"// Pre-check HCL/JSON job spec before submit\ntg.Services.each { s ->\n  s.Checks.each { c ->\n    if c.Expose && !(s.Connect != null && s.Connect.SidecarService != null)\n      throw new Error(\"check '${c.Name}' on service '${s.Name}' sets expose but service has no sidecar_proxy\")\n  }\n}","typeGuard":"func hasConnectSidecar(s *api.Service) bool {\n\treturn s != nil && s.Connect != nil && s.Connect.SidecarService != nil\n}","tryCatchPattern":"// golang\nerr := client.Jobs().Validate(job)\nif err != nil && strings.Contains(err.Error(), \"requires use of sidecar_proxy\") {\n\t// amend job spec: add connect.sidecar_service\n}","preventionTips":["Always pair expose = true with connect { sidecar_service {} } in the same service block.","Lint job specs with nomad job validate before submit in CI.","Remember expose is group-service-only; keep exposed checks at group level.","Keep the sidecar proxy resource stanza when copying expose examples."],"tags":["nomad","consul-connect","job-validation","expose-check"],"backgroundTag":"expose-check-requires-sidecar","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}