netbirdio/netbird · warning
invalid filter: %w
Error message
invalid filter: %w
What it means
The capture command validates its positional filter with capture.ParseFilter before sending it to the daemon, and this error wraps the parser's rejection. The accepted grammar is a tcpdump-style subset: host IP, src/dst combined with host/port/net, bare port NUM, net PREFIX/LEN, protocol keywords (tcp, udp, icmp, icmp6, ip, ip6, proto NUM), and the boolean operators and/or/not with parentheses. Anything outside that grammar — unsupported tcpdump keywords, hostnames instead of literal IPs, malformed addresses — is rejected client-side before any RPC.
Source
Thrown at client/cmd/capture.go:113
} else {
cmd.PrintErrf("Capturing packets (pcap)... Press Ctrl+C to stop.\n")
}
streamErr := streamCapture(ctx, cmd, stream, out)
cleanupErr := cleanup()
if streamErr != nil {
return streamErr
}
return cleanupErr
}
func buildCaptureRequest(cmd *cobra.Command, args []string) (*proto.StartCaptureRequest, error) {
req := &proto.StartCaptureRequest{}
if len(args) > 0 {
expr := strings.Join(args, " ")
if _, err := capture.ParseFilter(expr); err != nil {
return nil, fmt.Errorf("invalid filter: %w", err)
}
req.FilterExpr = expr
}
if snap, _ := cmd.Flags().GetUint32("snap-len"); snap > 0 {
req.SnapLen = snap
}
if d, _ := cmd.Flags().GetDuration("duration"); d != 0 {
if d < 0 {
return nil, fmt.Errorf("duration must not be negative")
}
req.Duration = durationpb.New(d)
}
req.Verbose, _ = cmd.Flags().GetBool("verbose")
req.Ascii, _ = cmd.Flags().GetBool("ascii")
outPath, _ := cmd.Flags().GetString("output")
forcePcap, _ := cmd.Flags().GetBool("pcap")View on GitHub (pinned to 93e97f4bf1)
Solutions
- Re-read the grammar in the error's source file (util/capture/filter.go): rewrite the filter using only host/src/dst/port/net/tcp/udp/icmp/icmp6/ip/ip6/proto with and/or/not
- Replace hostnames with literal IP addresses (resolve them yourself first: dig +short myhost)
- Fix the specific parse error text — it names the offending token and position for unexpected-token cases
- Test simpler sub-expressions incrementally: 'tcp' then 'host 10.0.0.1' then combine with and/or
Example fix
# before: unsupported keywords and a hostname netbird debug capture portrange 80-90 or host api.example.com # after: literal IPs and supported keywords netbird debug capture (port 80 or port 90) or host 203.0.113.10
Defensive patterns
Strategy: validation
Validate before calling
// Validate before starting anything, exactly as the CLI does:
if _, err := capture.ParseFilter(expr); err != nil {
return fmt.Errorf("invalid filter: %w", err)
} Prevention
- Restrict filters to the documented grammar: host/src/dst/port/net + tcp/udp/icmp/icmp6/ip/ip6/proto with and/or/not
- Resolve hostnames to IPs before building the expression — the parser accepts literal addresses only
- Build complex filters incrementally, testing each sub-expression as you add it
When it happens
Trigger: Using tcpdump keywords the parser does not implement: portrange, less, greater, vlan, arp, ether host, 'port 443 and port 80' with duplicated qualifiers; passing a hostname (host myserver.example.com) where only a literal IP is accepted; an IP typo like 10.0.0 or 10.0.0.256; unbalanced parentheses '(host 10.0.0.1'; trailing tokens the parser did not consume (unexpected token ... at position N).
Common situations: Copy-pasting a working tcpdump expression into netbird debug capture; assuming full BPF support because the syntax looks like tcpdump; shell quoting issues that split the expression (usually fine since args are rejoined with spaces); using net 10.0.0.0/24 vs bare CIDR '10.0.0.0/24' which parses as a term only under net/host context.
Related errors
- duration must not be negative
- parse filter: %w
- write output: %w
- create output file: %w
- close output file: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/a0f1b15f4eaeb56e.
Report an issue: GitHub.