{"record":{"id":"d5128c348f140ac4","repo":"netbirdio/netbird","slug":"create-capture-session-w","errorCode":null,"errorMessage":"create capture session: %w","messagePattern":"create capture session: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/embed/embed.go","lineNumber":577,"sourceCode":"\n\tvar matcher capture.Matcher\n\tif opts.Filter != \"\" {\n\t\tm, err := capture.ParseFilter(opts.Filter)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"parse filter: %w\", err)\n\t\t}\n\t\tmatcher = m\n\t}\n\n\tsess, err := capture.NewSession(capture.Options{\n\t\tOutput:     opts.Output,\n\t\tTextOutput: opts.TextOutput,\n\t\tMatcher:    matcher,\n\t\tVerbose:    opts.Verbose,\n\t\tASCII:      opts.ASCII,\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"create capture session: %w\", err)\n\t}\n\n\tif err := engine.SetCapture(sess); err != nil {\n\t\tsess.Stop()\n\t\treturn nil, fmt.Errorf(\"set capture: %w\", err)\n\t}\n\n\treturn &CaptureSession{sess: sess, engine: engine}, nil\n}\n\n// StopCapture stops the active capture session if one is running.\nfunc (c *Client) StopCapture() error {\n\tengine, err := c.getEngine()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn engine.SetCapture(nil)\n}","sourceCodeStart":559,"sourceCodeEnd":595,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/embed/embed.go#L559-L595","documentation":"After filter parsing, StartCapture calls capture.NewSession, which requires at least one output sink: CaptureOptions.Output (an io.Writer receiving pcap bytes) or CaptureOptions.TextOutput (an io.Writer receiving human-readable packet lines). With neither set, NewSession fails with \"at least one output sink required\", which is what this wrapper reports. The session also derives snap length and buffer size from defaults when unset, so those cannot cause this error.","triggerScenarios":"StartCapture(CaptureOptions{Filter: \"tcp\", Verbose: true}) where both Output and TextOutput are nil. Typical when a caller only wants Stats() afterwards, or when a UI forwards verbose/ascii flags but forgets to wire the writer.","commonSituations":"Embedding the client and intending to poll CaptureStats without capturing output; refactoring that drops the writer argument; passing a writer via a custom struct field the API does not read.","solutions":["Set CaptureOptions.TextOutput to any io.Writer (even io.Discard during tests) or set Output for pcap capture","Validate the options struct before calling StartCapture so callers get a domain-specific error"],"exampleFix":"// before\nsess, err := client.StartCapture(netbird.CaptureOptions{Filter: filter, Verbose: true})\n\n// after\nvar buf bytes.Buffer\nsess, err := client.StartCapture(netbird.CaptureOptions{Filter: filter, Verbose: true, TextOutput: &buf})","handlingStrategy":"validation","validationCode":"func validCaptureOpts(o netbird.CaptureOptions) error {\n    if o.Output == nil && o.TextOutput == nil {\n        return errors.New(\"capture requires Output or TextOutput\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"sess, err := client.StartCapture(opts)\nif err != nil {\n    if strings.Contains(err.Error(), \"create capture session\") {\n        // programmer error in options; fix caller, no retry\n    }\n}","preventionTips":["Always set TextOutput (io.Discard in tests) when capturing","Wrap capture setup in your own helper that enforces the sink requirement once"],"tags":["go","capture","io","embed","validation"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}