{"record":{"id":"b29559c1fbed287a","repo":"netbirdio/netbird","slug":"write-output-w","errorCode":null,"errorMessage":"write output: %w","messagePattern":"write output: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/cmd/capture.go","lineNumber":152,"sourceCode":"\treturn req, nil\n}\n\nfunc streamCapture(ctx context.Context, cmd *cobra.Command, stream proto.DaemonService_StartCaptureClient, out io.Writer) error {\n\tfor {\n\t\tpkt, err := stream.Recv()\n\t\tif err != nil {\n\t\t\tif ctx.Err() != nil {\n\t\t\t\tcmd.PrintErrf(\"\\nCapture stopped.\\n\")\n\t\t\t\treturn nil //nolint:nilerr // user interrupted\n\t\t\t}\n\t\t\tif err == io.EOF {\n\t\t\t\tcmd.PrintErrf(\"\\nCapture finished.\\n\")\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn handleCaptureError(err)\n\t\t}\n\t\tif _, err := out.Write(pkt.GetData()); err != nil {\n\t\t\treturn fmt.Errorf(\"write output: %w\", err)\n\t\t}\n\t}\n}\n\n// captureOutput returns the writer for capture data and a cleanup function\n// that finalizes the file. Errors from the cleanup must be propagated.\nfunc captureOutput(cmd *cobra.Command) (io.Writer, func() error, error) {\n\toutPath, _ := cmd.Flags().GetString(\"output\")\n\tif outPath == \"\" {\n\t\treturn os.Stdout, func() error { return nil }, nil\n\t}\n\n\tf, err := os.CreateTemp(filepath.Dir(outPath), filepath.Base(outPath)+\".*.tmp\")\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"create output file: %w\", err)\n\t}\n\ttmpPath := f.Name()\n\treturn f, func() error {","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/capture.go#L134-L170","documentation":"Raised while streaming captured packets: writing a packet's bytes to the chosen output writer failed. The writer is either os.Stdout (default, or when piping to tshark/tcpdump with --pcap) or the temp file created for --output. The error wraps the underlying write error, so the cause is almost always a closed consumer (EPIPE) or a disk/filesystem problem (ENOSPC, EDQUOT).","triggerScenarios":"Piping the pcap stream into a consumer that exits early: netbird debug capture --pcap | head -c 1M, or tshark/tcpdump terminating on their own error, closing the pipe; redirecting to a file on a full disk (text or pcap mode); the temp output file's filesystem filling mid-capture.","commonSituations":"Using head or a viewer that stops reading; consumer crashes and the next Write gets EPIPE (Go then raises SIGPIPE handling nuances but the write error surfaces here); long captures on a small tmpfs; quota exceeded on the output directory.","solutions":["If piping, keep the consumer alive for the whole capture or drop the pipe and use --output file.pcap","Check disk space on the output path: df -h <dir> and free space / raise quota","If the consumer exiting early is intended, treat the capture as done — run with a bounded --duration instead of relying on the consumer to stop it","Re-run writing to a location with capacity, optionally with --snap-len to cut bytes per packet"],"exampleFix":"# before: consumer exits early -> broken pipe\nnetbird debug capture --pcap | head -c 1000000 > sample.pcap\n\n# after: bound the capture itself, write to a file\nnetbird debug capture --pcap -d 10s -o sample.pcap","handlingStrategy":"validation","validationCode":"// If writing to a file, check capacity first:\nvar st syscall.Statfs_t\nif err := syscall.Statfs(filepath.Dir(outPath), &st); err == nil && st.Bavail*uint64(st.Bsize) < minFree {\n    return fmt.Errorf(\"insufficient space in %s\", filepath.Dir(outPath))\n}","typeGuard":null,"tryCatchPattern":"// In Go, treat EPIPE on stdout as a normal early stop:\nif _, err := out.Write(pkt.GetData()); err != nil {\n    if errors.Is(err, syscall.EPIPE) {\n        return nil // consumer went away; capture is done\n    }\n    return fmt.Errorf(\"write output: %w\", err)\n}","preventionTips":["Use --output instead of pipes when the consumer may exit early","Bound captures with --duration so unbounded streams cannot fill a disk","Set --snap-len to cap per-packet bytes when only headers are needed"],"tags":["capture","io","epipe","disk","cli","stdout"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}