micro/go-micro · error

tcp dial %s: %w

Error message

tcp dial %s: %w

What it means

TCPCheck returns a CheckFunc used by the health package that attempts net.DialTimeout to the given address. If the dial fails (connection refused, timeout, DNS failure, network unreachable), the error is wrapped as "tcp dial <addr>: <underlying error>" so the health check reports failure with the address and cause included.

Source

Thrown at health/health.go:269

// PingCheck creates a check from a ping function (like sql.DB.Ping)
func PingCheck(ping func() error) CheckFunc {
	return func(ctx context.Context) error {
		return ping()
	}
}

// PingContextCheck creates a check from a ping function that accepts context
func PingContextCheck(ping func(context.Context) error) CheckFunc {
	return ping
}

// TCPCheck creates a check that verifies TCP connectivity
func TCPCheck(addr string, timeout time.Duration) CheckFunc {
	return func(ctx context.Context) error {
		conn, err := net.DialTimeout("tcp", addr, timeout)
		if err != nil {
			return fmt.Errorf("tcp dial %s: %w", addr, err)
		}
		conn.Close()
		return nil
	}
}

// HTTPCheck creates a check that verifies an HTTP endpoint returns 200
func HTTPCheck(url string, timeout time.Duration) CheckFunc {
	return func(ctx context.Context) error {
		client := &http.Client{Timeout: timeout}
		req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
		if err != nil {
			return err
		}
		resp, err := client.Do(req)
		if err != nil {
			return fmt.Errorf("http get %s: %w", url, err)
		}

View on GitHub (pinned to 24529f1404)

Solutions

  1. Read the wrapped cause: connection refused means nothing is listening; i/o timeout means network/timeout issue; no such host means DNS failure.
  2. Verify the target service is running and listening on the configured addr (netstat/ss or telnet/nc).
  3. Correct the address in configuration if the host/port is wrong.
  4. Increase the timeout parameter, and configure the health framework's retry/interval so transient startup delays don't fail the check.

Example fix

// before
check := health.TCPCheck("db:5432", 100*time.Millisecond)
// after
check := health.TCPCheck("postgres.default.svc.cluster.local:5432", 3*time.Second)
Defensive patterns

Strategy: retry

Validate before calling

// probe before registering the check
conn, err := net.DialTimeout("tcp", addr, timeout)
if err != nil {
    return fmt.Errorf("dependency %s unreachable: %w", addr, err)
}
conn.Close()

Try / catch

if err := check(ctx); err != nil {
    var nerr net.Error
    if errors.As(err, &nerr) && nerr.Timeout() {
        // increase timeout / retry
    } else if strings.Contains(err.Error(), "connection refused") {
        // dependency down: wait for startup, then retry
    }
}

Prevention

When it happens

Trigger: The health check runs and net.DialTimeout cannot establish a TCP connection within the timeout: target service is down, listening on a different port, DNS name doesn't resolve, firewall drops packets, or the timeout is shorter than the connection setup time.

Common situations: Dependency service crashed or not yet started (startup ordering); wrong host/port in config; Kubernetes service DNS not ready; firewall/security group blocking the port; timeout set too aggressively (e.g. 100ms) for a remote dependency.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01). Data as JSON: /api/errors/86990700ba606305. Report an issue: GitHub.