hashicorp/nomad · error

Check %s invalid: tcp checks are not valid for Connect enabl

Error message

Check %s invalid: tcp checks are not valid for Connect enabled services

What it means

This error is raised in validateConsulService when a TCP check is attached to a Consul Connect-enabled service (service has a connect stanza with a sidecar_service). TCP checks hit the service's loopback-bound address inside the network namespace and cannot work, so Nomad rejects the configuration up front.

Source

Thrown at nomad/structs/services.go:865

	return nil
}

// validateConsulService performs validation on a service which is using the
// consul provider.
func (s *Service) validateConsulService(mErr *multierror.Error) {
	// check checks
	for _, c := range s.Checks {
		// validate the check port
		if err := s.validateCheckPort(c); err != nil {
			mErr.Errors = append(mErr.Errors, err)
			continue
		}

		// TCP checks against a Consul Connect enabled service are not supported
		// due to the service being bound to the loopback interface inside the
		// network namespace
		if c.Type == ServiceCheckTCP && s.Connect != nil && s.Connect.SidecarService != nil {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Check %s invalid: tcp checks are not valid for Connect enabled services", c.Name))
			continue
		}

		// validate the consul check
		if err := c.validateConsul(); err != nil {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Check %s invalid: %v", c.Name, err))
		}
	}

	// validate the consul service kind
	switch api.ServiceKind(s.Kind) {
	case api.ServiceKindTypical,
		api.ServiceKindAPIGateway,
		api.ServiceKindIngressGateway,
		api.ServiceKindMeshGateway,
		api.ServiceKindTerminatingGateway:
	default:
		mErr.Errors = append(mErr.Errors, fmt.Errorf("Service %s kind must be one of consul service kind or empty", s.Name))

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Change the check type to "http" or "grpc" targeting the sidecar proxy (e.g. address_mode = "driver", port pointing at the proxy port).
  2. Remove the TCP check and rely on Consul's mesh health or an application-level HTTP check.
  3. Drop the connect stanza if Connect is not actually needed.

Example fix

// before
service {
  name = "web"
  connect { sidecar_service {} }
  check { type = "tcp" interval = "10s" timeout = "2s" }
}
// after
service {
  name = "web"
  connect { sidecar_service {} }
  check {
    type = "http"
    path = "/health"
    port = "9090"
    interval = "10s"
    timeout = "2s"
  }
}
Defensive patterns

Strategy: validation

Validate before calling

func validateConnectChecks(svc Service) error {
  if svc.Connect != nil && svc.Connect.SidecarService != nil {
    for _, c := range svc.Checks {
      if c.Type == "tcp" {
        return fmt.Errorf("check %q: tcp not allowed on Connect-enabled services", c.Name)
      }
    }
  }
  return nil
}

Prevention

When it happens

Trigger: A service stanza with `connect { sidecar_service {} }` and a check of type = "tcp".

Common situations: Adding a standard TCP health check to a service that was later converted to use Consul Connect; migrating a plain service to Connect and keeping existing checks.

Related errors


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