hashicorp/nomad · error

%s: %w: group %q has %d networks

Error message

%s: %w: group %q has %d networks

What it means

Nomad's connect validation requires each task group using Consul Connect (sidecar, gateway, or expose checks) to declare exactly one group network block. The job submission is rejected when the group has zero or more than one network. This invariant exists because connect sidecars/gateways are attached to a single group network namespace and Nomad cannot pick which network block applies.

Source

Thrown at nomad/job_endpoint_hook_connect.go:636

func transparentProxyPortLabelValidate(g *structs.TaskGroup, portLabel string) bool {
	if _, err := strconv.ParseUint(portLabel, 10, 16); err == nil {
		return true
	}

	for _, network := range g.Networks {
		for _, reservedPort := range network.ReservedPorts {
			if reservedPort.Label == portLabel {
				return true
			}
		}
	}
	return false
}

func groupConnectNetworkModeValidate(g *structs.TaskGroup, errorPrefix string, allowHost bool) error {
	if nn := len(g.Networks); nn != 1 {
		return fmt.Errorf("%s: %w: group %q has %d networks",
			errorPrefix, ErrConnectRequireOneNetwork, g.Name, nn)
	}

	mode := g.Networks[0].Mode
	if mode == "bridge" || (allowHost && mode == "host") || strings.HasPrefix(mode, "cni/") {
		return nil
	}

	// helpful error message
	allowed := `"bridge" or "cni/*"`
	if allowHost {
		allowed = `"bridge", "host", or "cni/*"`
	}
	return fmt.Errorf("%s: %w: group %q uses network mode %q; must be %s",
		errorPrefix, ErrConnectInvalidNetworkMode, g.Name, mode, allowed)
}

func groupConnectSidecarValidate(g *structs.TaskGroup, s *structs.Service) error {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Ensure the task group has exactly one group-level `network` block, moving any ports into that single block.
  2. Remove duplicate network blocks by merging their port/dns settings into one stanza.
  3. If the group does not need connect, remove the connect sidecar service/gateway config so validation is skipped.

Example fix

// before
group "api" {
  network {
    port "http" {}
  }
  network {
    port "metrics" {}
  }
  service {
    name = "api"
    connect { sidecar_service {} }
  }
}
// after
group "api" {
  network {
    port "http" {}
    port "metrics" {}
  }
  service {
    name = "api"
    connect { sidecar_service {} }
  }
}
Defensive patterns

Strategy: validation

Validate before calling

function validateConnectGroupNetworks(group) {
  const nets = (group.networks || []);
  if (nets.length !== 1) {
    throw new Error(`group "${group.name}" has ${nets.length} networks; connect groups need exactly one`);
  }
}

Type guard

function hasExactlyOneNetwork(g) { return Array.isArray(g.networks) && g.networks.length === 1; }

Prevention

When it happens

Trigger: Submitting a job where a task group with a connect sidecar service (or gateway, or expose block) has no `network {}` block, or has two or more `network {}` blocks at the group level; groupConnectNetworkModeValidate returns this immediately on `len(g.Networks) != 1`.

Common situations: Jobs converted from pre-connect templates missing the group network stanza; users adding a second network block for an extra port or DNS setting on a connect-enabled group; merging partial HCL that appends networks; programmatically generated jobs appending networks per service.

Related errors


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