amir20/dozzle · error

failed to connect to

Error message

failed to connect to %s: %w

What it means

NewClient wraps the error from grpc.NewClient as "failed to connect to %s: %w" when the client connection cannot even be created for the endpoint. Note grpc.NewClient is lazy, so this is almost always an invalid target string (bad scheme/address) rather than the agent being down; runtime connectivity failures surface later through rpcErrToErr.

Solutions

  1. Fix the endpoint to host:port form (default agent port 7007), e.g. "agent-host:7007" or "host|Friendly Name"
  2. Drop any URL scheme (no https://) and bracket IPv6 addresses like [fd00::1]:7007
  3. Verify the endpoint string comes from the DOZZLE_AGENT config/labels without extra characters (spaces, quotes)
  4. If the agent is unreachable at runtime, check the agent container is up and port 7007 is published; the error appears on first RPC

Example fix

// before
client, _ := agent.NewClient("https://agent:7007|Agent", certs)
// after
client, err := agent.NewClient("agent:7007|Agent", certs)
if err != nil { log.Fatal().Err(err).Msg("agent endpoint invalid") }
Defensive patterns

Strategy: validation

Validate before calling

if _, _, _, err := agent.ParseEndpoint(endpoint); err != nil { return err }
host, port, ok := strings.Cut(endpoint, "|")
if !strings.Contains(host, ":") { return fmt.Errorf("endpoint %q must be host:port", endpoint) }

Type guard

func validEndpoint(e string) bool { addr, _, _, err := agent.ParseEndpoint(e); return err == nil && strings.Contains(addr, ":") }

Try / catch

client, err := agent.NewClient(endpoint, certs)
if err != nil {
    return fmt.Errorf("agent %s unreachable, check address and port 7007: %w", endpoint, err)
}

Prevention

When it happens

Trigger: agent.NewClient(endpoint, certs) where the parsed address is malformed for gRPC's resolver, e.g. missing host:port, an unsupported scheme prefix, or empty after parsing "|name|group"-style endpoints.

Common situations: Docker label/env configured endpoint like "192.168.1.5" without port instead of "192.168.1.5:7007"; DNS names with typos; IPv6 addresses not bracketed; accidentally leaving the DOZZLE_AGENT address as a URL (https://host:7007).

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 amir20/dozzle@d9463cbe21 (2026-09-07). Data as JSON: /api/errors/ea81b18f83cea2e0. Report an issue: GitHub.

Appendix: source

Thrown at internal/agent/client.go:72

		RootCAs:            caCertPool,
		InsecureSkipVerify: true, // Set to true if the server's hostname does not match the certificate
	}

	// Create the gRPC transport credentials
	creds := credentials.NewTLS(tlsConfig)

	opts = append(opts,
		grpc.WithTransportCredentials(creds),
		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(10*1024*1024), grpc.UseCompressor(gzip.Name)),
		grpc.WithKeepaliveParams(keepalive.ClientParameters{
			Time:                30 * time.Second,
			Timeout:             10 * time.Second,
			PermitWithoutStream: true,
		}),
	)
	conn, err := grpc.NewClient(endpoint, opts...)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to %s: %w", endpoint, err)
	}

	client := pb.NewAgentServiceClient(conn)

	return &Client{
		client:       client,
		conn:         conn,
		endpoint:     endpoint,
		nameOverride: nameOverride,
		group:        group,
	}, nil
}

// ParseEndpoint splits an agent endpoint of the form "address|name|group" into
// its parts. Name and group are optional; address is required.
func ParseEndpoint(endpoint string) (string, string, string, error) {
	parts := strings.Split(endpoint, "|")
	if len(parts) > 3 || parts[0] == "" {

View on GitHub (pinned to d9463cbe21)