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
- Rewrite the filter using only the supported grammar: host IP, src/dst target, port NUM, net PREFIX, protocol keywords, and/or/not, parentheses
- Validate user-supplied filters by calling capture.ParseFilter (or a dry-run StartCapture/StopCapture) before the real capture
- 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
- Validate user-supplied filters server-side before passing them to StartCapture
- Keep a whitelist of filter shapes your UI generates
- Document the supported grammar (no ether/portrange/byte-offset primitives) next to the input field
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
- create capture session: %w
- unclosed parenthesis
- invalid filter: %w
- connector type change not allowed
- service name is required
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/354e7ff0b3e73be5.
Report an issue: GitHub.