netbirdio/netbird · error

create output file: %w

Error message

create output file: %w

What it means

captureOutput creates the pcap output as a temp file in the same directory as the requested --output path (basename + .*.tmp) via os.CreateTemp, and this wraps that failure. Creating in the target directory is deliberate — the finalize step renames the temp file onto the final path atomically, so a temp file on another filesystem would break the rename. The error means the directory itself could not host a new file.

Source

Thrown at client/cmd/capture.go:167

			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 {
		var merr *multierror.Error
		if err := f.Close(); err != nil {
			merr = multierror.Append(merr, fmt.Errorf("close output file: %w", err))
		}
		fi, statErr := os.Stat(tmpPath)
		if statErr != nil || fi.Size() == 0 {
			if rmErr := os.Remove(tmpPath); rmErr != nil && !os.IsNotExist(rmErr) {
				merr = multierror.Append(merr, fmt.Errorf("remove empty output file: %w", rmErr))
			}
			return nberrors.FormatErrorOrNil(merr)
		}
		if err := os.Rename(tmpPath, outPath); err != nil {
			merr = multierror.Append(merr, fmt.Errorf("rename output file: %w", err))
			return nberrors.FormatErrorOrNil(merr)
		}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Create the parent directory first: mkdir -p /var/captures, then rerun with -o /var/captures/x.pcap
  2. Check write permission on the directory (ls -ld, id) and choose a writable path or run with appropriate privileges
  3. Verify the filesystem is not read-only or full: mount | grep <dir>, df -h <dir>
  4. Prefer an absolute path for -o to avoid cwd surprises

Example fix

# before: parent directory does not exist
netbird debug capture -o /var/captures/x.pcap

# after: create it first
mkdir -p /var/captures && netbird debug capture -o /var/captures/x.pcap
Defensive patterns

Strategy: validation

Validate before calling

// Verify writability of the output directory before starting the capture:
dir := filepath.Dir(outPath)
if info, err := os.Stat(dir); err != nil || !info.IsDir() {
    return fmt.Errorf("output directory %s missing", dir)
}
probe, err := os.CreateTemp(dir, ".probe")
if err != nil {
    return fmt.Errorf("cannot write in %s: %v", dir, err)
}
os.Remove(probe.Name())

Prevention

When it happens

Trigger: --output points into a directory that does not exist (netbird debug capture -o /var/captures/x.pcap with /var/captures missing); no write permission on the directory (running unprivileged, writing to /root or a root-owned dir); read-only or full filesystem (EROFS, ENOSPC); -o with a bare filename while cwd is not writable.

Common situations: Typo in the output path; expecting the tool to mkdir -p parent directories (it does not); running as non-root where the CLI default dirs are root-owned; container/CI with a read-only volume as the output location.

Related errors


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