netbirdio/netbird · error

trace failed: %v

Error message

trace failed: %v

What it means

Thrown by tracePacket (client/cmd/trace.go:113) after the daemon RPC proto.DaemonServiceClient.TracePacket returns an error. The CLI collapses it with status.Convert(err).Message(), which keeps only the gRPC message text and discards the status code - so an Unimplemented from an old daemon and a validation error from a running one look the same. The trace request is validated and executed by the daemon (source/dest IP parsing, firewall rule evaluation), not by the CLI.

Source

Thrown at client/cmd/trace.go:113

	if err != nil {
		return err
	}
	defer conn.Close()

	client := proto.NewDaemonServiceClient(conn)
	resp, err := client.TracePacket(cmd.Context(), &proto.TracePacketRequest{
		SourceIp:        args[1],
		DestinationIp:   args[2],
		Protocol:        protocol,
		SourcePort:      uint32(sport),
		DestinationPort: uint32(dport),
		Direction:       direction,
		TcpFlags:        tcpFlags,
		IcmpType:        &icmpType,
		IcmpCode:        &icmpCode,
	})
	if err != nil {
		return fmt.Errorf("trace failed: %v", status.Convert(err).Message())
	}

	printTrace(cmd, args[1], args[2], protocol, sport, dport, resp)
	return nil
}

func printTrace(cmd *cobra.Command, src, dst, proto string, sport, dport uint16, resp *proto.TracePacketResponse) {
	cmd.Printf("Packet trace %s:%d → %s:%d (%s)\n\n", src, sport, dst, dport, strings.ToUpper(proto))

	for _, stage := range resp.Stages {
		if stage.ForwardingDetails != nil {
			cmd.Printf("%s: %s [%s]\n", stage.Name, stage.Message, *stage.ForwardingDetails)
		} else {
			cmd.Printf("%s: %s\n", stage.Name, stage.Message)
		}
	}

	disposition := map[bool]string{

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Upgrade and restart the daemon to the same version as the CLI ('netbird service install' + restart) if the message mentions an unknown/unimplemented method
  2. Bring the client up first ('netbird up') and confirm with 'netbird status' before tracing
  3. Use plain host IPs (or the 'self' keyword shown in the command examples) for source and destination
  4. Re-run with the daemon log open - the daemon-side message usually carries the specific parse or evaluation failure

Example fix

// before
return fmt.Errorf("trace failed: %v", status.Convert(err).Message())

// after: keep the gRPC code so Unimplemented is distinguishable
if st, ok := status.FromError(err); ok && st.Code() == codes.Unimplemented {
    return fmt.Errorf("trace failed: daemon does not support TracePacket, restart it with the current binary: %s", st.Message())
}
return fmt.Errorf("trace failed: %w", err)
Defensive patterns

Strategy: try-catch

Validate before calling

// cheap preconditions before tracing
if net.ParseIP(src) == nil && src != "self" {
    log.Fatalf("invalid source ip %q", src)
}
if net.ParseIP(dst) == nil && dst != "self" {
    log.Fatalf("invalid destination ip %q", dst)
}

Type guard

func isDaemonUnimplemented(err error) bool {
    st, ok := gstatus.FromError(err)
    return ok && st.Code() == codes.Unimplemented
}

Try / catch

resp, err := client.TracePacket(ctx, req)
if err != nil {
    if st, ok := gstatus.FromError(err); ok {
        switch st.Code() {
        case codes.Unimplemented:
            return fmt.Errorf("daemon too old for trace: restart it with the current binary")
        case codes.Unavailable:
            return fmt.Errorf("daemon unreachable: %w", err)
        }
    }
    return fmt.Errorf("trace failed: %w", err)
}

Prevention

When it happens

Trigger: Calling 'netbird debug trace' when: the daemon binary predates the TracePacket RPC (codes.Unimplemented); SourceIp/DestinationIp args are not valid IPs or the 'self' keyword; the firewall manager is not running because the client is down; or the context is canceled mid-trace.

Common situations: CLI upgraded but service not restarted (old daemon still serving); tracing while 'netbird status' shows disconnected; passing CIDR notation (192.168.1.0/24) where a host IP is required.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/d563dd5dcf7888ed. Report an issue: GitHub.