{"record":{"id":"5d1010c44bb8645d","repo":"hashicorp/nomad","slug":"consul-connect-services-q-and-q-in-group-q-usin","errorCode":null,"errorMessage":"Consul Connect services %q and %q in group %q using same address for upstreams (%s)","messagePattern":"Consul Connect services %q and %q in group %q using same address for upstreams \\((.+?)\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/job_endpoint_hook_connect.go","lineNumber":587,"sourceCode":"\tlisteners := make(map[string]string) // address or path-> service\n\n\tvar connectBlockCount int\n\tvar hasTproxy bool\n\n\tfor _, service := range services {\n\t\tif service.Connect != nil {\n\t\t\tconnectBlockCount++\n\t\t}\n\t\tif service.Connect.HasSidecar() && service.Connect.SidecarService.Proxy != nil {\n\t\t\tfor _, up := range service.Connect.SidecarService.Proxy.Upstreams {\n\t\t\t\tvar listener string\n\t\t\t\tif up.LocalBindSocketPath == \"\" {\n\t\t\t\t\tlistener = net.JoinHostPort(up.LocalBindAddress, strconv.Itoa(up.LocalBindPort))\n\t\t\t\t} else {\n\t\t\t\t\tlistener = up.LocalBindSocketPath\n\t\t\t\t}\n\t\t\t\tif s, exists := listeners[listener]; exists {\n\t\t\t\t\treturn fmt.Errorf(\n\t\t\t\t\t\t\"Consul Connect services %q and %q in group %q using same address for upstreams (%s)\",\n\t\t\t\t\t\tservice.Name, s, g.Name, listener,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tlisteners[listener] = service.Name\n\t\t\t}\n\n\t\t\tif tp := service.Connect.SidecarService.Proxy.TransparentProxy; tp != nil {\n\t\t\t\thasTproxy = true\n\t\t\t\tfor _, net := range g.Networks {\n\t\t\t\t\tif !net.DNS.IsZero() && !tp.NoDNS {\n\t\t\t\t\t\treturn fmt.Errorf(\n\t\t\t\t\t\t\t\"Consul Connect transparent proxy cannot be used with network.dns unless no_dns=true\")\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor _, portLabel := range tp.ExcludeInboundPorts {\n\t\t\t\t\tif !transparentProxyPortLabelValidate(g, portLabel) {\n\t\t\t\t\t\treturn fmt.Errorf(","sourceCodeStart":569,"sourceCodeEnd":605,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/job_endpoint_hook_connect.go#L569-L605","documentation":"Nomad's connect job validation hook (`groupConnectUpstreamsValidate`) builds a listener string (host:port or unix socket path) for every Consul Connect upstream in a task group and rejects duplicates. Two Connect services in the same group declare upstreams that would bind to the exact same local address, so the second would fail at runtime; the job mutator/validator throws this error at `nomad job run`/plan time instead.","triggerScenarios":"Submit a job (nomad job run/plan/validate, or the jobs API Validate endpoint) where a task group contains two `service { connect {} }` blocks whose `upstream` blocks resolve to the identical LocalBindAddress:LocalBindPort or LocalBindSocketPath. This fires in groupConnectUpstreamsValidate when `listeners[listener]` already contains the listener string.","commonSituations":"Copy-pasting upstream blocks and forgetting to change the default local bind port (8443 or the service port); two upstreams to the same destination service declared under different service blocks, both using the default local bind address; groups with many connect services where each upstream reuses the socket path.","solutions":["Give each upstream a distinct local bind port: set `upstream { local_bind_port = <unique-port> }` on one of the conflicting upstreams.","If both upstreams target the same destination service, remove the duplicate upstream and reference the service once.","Use distinct `local_bind_socket_path` values if you bind upstreams via unix sockets instead of TCP.","Keep the default bind address but vary ports, or set a unique `local_bind_address` per upstream."],"exampleFix":"// before\nupstream {\n  destination_name = \"count-api\"\n}\nupstream {\n  destination_name = \"count-api2\"\n  # local_bind_port defaults to 8443 too -> collision\n}\n// after\nupstream {\n  destination_name = \"count-api\"\n}\nupstream {\n  destination_name = \"count-api2\"\n  local_bind_port  = 9191\n}","handlingStrategy":"validation","validationCode":"// Before submitting, ensure every upstream in each group has a unique bind point.\nfunction validateUpstreamBinds(groups) {\n  for (const g of groups) {\n    const seen = new Map();\n    for (const svc of g.services.filter(s => s.connect)) {\n      for (const up of svc.connect.sidecar_service?.proxy?.upstreams ?? svc.connect.sidecar_service?.upstreams ?? []) {\n        const listener = up.local_bind_socket_path ?? `${up.local_bind_address ?? '127.0.0.1'}:${up.local_bind_port ?? 8443}`;\n        if (seen.has(listener)) throw new Error(`duplicate upstream bind ${listener} in group ${g.name}`);\n        seen.set(listener, svc.name);\n      }\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always set local_bind_port explicitly on every upstream; never rely on defaults when a group has multiple upstreams.","Run `nomad job validate <file>` in CI before any `nomad job run`.","Keep a convention of allocating unique bind port ranges per service in a group.","When using socket paths, derive them from the service name to guarantee uniqueness."],"tags":["consul-connect","nomad-job-spec","port-conflict","validation"],"backgroundTag":"address-already-in-use","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"}