netbirdio/netbird · error

write output: %w

Error message

write output: %w

What it means

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).

Source

Thrown at client/cmd/capture.go:152

	return req, nil
}

func streamCapture(ctx context.Context, cmd *cobra.Command, stream proto.DaemonService_StartCaptureClient, out io.Writer) error {
	for {
		pkt, err := stream.Recv()
		if err != nil {
			if ctx.Err() != nil {
				cmd.PrintErrf("\nCapture stopped.\n")
				return nil //nolint:nilerr // user interrupted
			}
			if err == io.EOF {
				cmd.PrintErrf("\nCapture finished.\n")
				return nil
			}
			return handleCaptureError(err)
		}
		if _, err := out.Write(pkt.GetData()); err != nil {
			return fmt.Errorf("write output: %w", err)
		}
	}
}

// captureOutput returns the writer for capture data and a cleanup function
// that finalizes the file. Errors from the cleanup must be propagated.
func captureOutput(cmd *cobra.Command) (io.Writer, func() error, error) {
	outPath, _ := cmd.Flags().GetString("output")
	if outPath == "" {
		return os.Stdout, func() error { return nil }, nil
	}

	f, err := os.CreateTemp(filepath.Dir(outPath), filepath.Base(outPath)+".*.tmp")
	if err != nil {
		return nil, nil, fmt.Errorf("create output file: %w", err)
	}
	tmpPath := f.Name()
	return f, func() error {

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. If piping, keep the consumer alive for the whole capture or drop the pipe and use --output file.pcap
  2. Check disk space on the output path: df -h <dir> and free space / raise quota
  3. 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
  4. Re-run writing to a location with capacity, optionally with --snap-len to cut bytes per packet

Example fix

# before: consumer exits early -> broken pipe
netbird debug capture --pcap | head -c 1000000 > sample.pcap

# after: bound the capture itself, write to a file
netbird debug capture --pcap -d 10s -o sample.pcap
Defensive patterns

Strategy: validation

Validate before calling

// If writing to a file, check capacity first:
var st syscall.Statfs_t
if err := syscall.Statfs(filepath.Dir(outPath), &st); err == nil && st.Bavail*uint64(st.Bsize) < minFree {
    return fmt.Errorf("insufficient space in %s", filepath.Dir(outPath))
}

Try / catch

// In Go, treat EPIPE on stdout as a normal early stop:
if _, err := out.Write(pkt.GetData()); err != nil {
    if errors.Is(err, syscall.EPIPE) {
        return nil // consumer went away; capture is done
    }
    return fmt.Errorf("write output: %w", err)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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