netbirdio/netbird · error

parse filter: %w

Error message

parse filter: %w

What it means

Client.StartCapture parses opts.Filter with capture.ParseFilter, a small tcpdump-like grammar (host/net/port/src/dst/proto/tcp/udp/icmp/icmp6/ip/ip6 combined with and/or/not and parentheses). The error is returned when the expression violates that grammar, e.g. an incomplete term or a trailing unparsed token ("unexpected token %q at position %d"). An empty filter is valid and means match-all, so this error only fires on a non-empty malformed expression.

Source

Thrown at client/embed/embed.go:564

	return engine.SetPerformance(internal.Performance{
		PreallocatedBuffersPerPool: t.PreallocatedBuffersPerPool,
	})
}

// StartCapture begins capturing packets on this client's tunnel device.
// Only one capture can be active at a time; starting a new one stops the previous.
// Call StopCapture (or CaptureSession.Stop) to end it.
func (c *Client) StartCapture(opts CaptureOptions) (*CaptureSession, error) {
	engine, err := c.getEngine()
	if err != nil {
		return nil, err
	}

	var matcher capture.Matcher
	if opts.Filter != "" {
		m, err := capture.ParseFilter(opts.Filter)
		if err != nil {
			return nil, fmt.Errorf("parse filter: %w", err)
		}
		matcher = m
	}

	sess, err := capture.NewSession(capture.Options{
		Output:     opts.Output,
		TextOutput: opts.TextOutput,
		Matcher:    matcher,
		Verbose:    opts.Verbose,
		ASCII:      opts.ASCII,
	})
	if err != nil {
		return nil, fmt.Errorf("create capture session: %w", err)
	}

	if err := engine.SetCapture(sess); err != nil {
		sess.Stop()
		return nil, fmt.Errorf("set capture: %w", err)

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Rewrite the filter using only the supported grammar: host IP, src/dst target, port NUM, net PREFIX, protocol keywords, and/or/not, parentheses
  2. Validate user-supplied filters by calling capture.ParseFilter (or a dry-run StartCapture/StopCapture) before the real capture
  3. Strip unsupported primitives like ether/link-layer qualifiers, portrange, or byte-offset expressions

Example fix

// before
sess, err := client.StartCapture(netbird.CaptureOptions{Filter: "ether host aa:bb:cc:dd:ee:ff", TextOutput: w})

// after
sess, err := client.StartCapture(netbird.CaptureOptions{Filter: "host 10.0.0.5 and tcp port 443", TextOutput: w})
Defensive patterns

Strategy: validation

Validate before calling

// dry-run parse before starting the capture
if opts.Filter != "" {
    if _, err := capture.ParseFilter(opts.Filter); err != nil {
        return fmt.Errorf("invalid capture filter: %w", err)
    }
}

Try / catch

sess, err := client.StartCapture(opts)
if err != nil {
    if strings.Contains(err.Error(), "parse filter") {
        // user-input problem: surface it, do not retry
        http.Error(w, err.Error(), http.StatusBadRequest)
    }
}

Prevention

When it happens

Trigger: StartCapture(CaptureOptions{Filter: "tcp port"}) (port with no number), "port abc", "host not.an.ip", unbalanced "(host 10.0.0.1", a leading "and tcp", or unsupported tcpdump primitives like "ether host ..", "portrange 1000-2000", "tcp[tcpflags] & .. != 0", "less 100".

Common situations: Copying a filter from a tcpdump/Wireshark cookbook into the embedded capture API; assuming full BPF/libpcap syntax is supported; user-supplied filter strings from a web UI reaching the API unvalidated.

Related errors


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